texture zebre plus sourie menu

This commit is contained in:
2025-12-19 16:22:09 +01:00
parent 844c64c7b6
commit 1b474fcbca
4 changed files with 767 additions and 86 deletions

513
README.md
View File

@ -1,4 +1,4 @@
# 🎮 Oulvic - Jeu de type Pac-Man # 🎮 OULVIC - Jeu de type Pac-Man
Un jeu de type Pac-Man développé en JavaScript vanilla par **Ludo** et **Syoul**. Un jeu de type Pac-Man développé en JavaScript vanilla par **Ludo** et **Syoul**.
@ -13,19 +13,25 @@ Un jeu de type Pac-Man développé en JavaScript vanilla par **Ludo** et **Syoul
5. [Comment jouer](#comment-jouer) 5. [Comment jouer](#comment-jouer)
6. [Architecture du code](#architecture-du-code) 6. [Architecture du code](#architecture-du-code)
7. [Documentation technique](#documentation-technique) 7. [Documentation technique](#documentation-technique)
8. [Systèmes avancés](#systèmes-avancés)
--- ---
## 📝 Description ## 📝 Description
**Oulvic** est un clone de Pac-Man développé entièrement en HTML5, CSS3 et JavaScript vanilla. Le jeu utilise l'élément `<canvas>` pour le rendu graphique et propose une expérience de jeu complète avec : **OULVIC** est un clone de Pac-Man développé entièrement en HTML5, CSS3 et JavaScript vanilla. Le jeu utilise l'élément `<canvas>` pour le rendu graphique et propose une expérience de jeu complète avec :
- Un personnage jouable aux couleurs changeantes - Un personnage jouable personnalisable (couleurs et formes)
- 4 fantômes avec intelligence artificielle - Système de fantômes avec IA avancée et types variés
- 3 labyrinthes différents - 3 labyrinthes différents qui alternent
- Système de niveaux progressifs - Système de niveaux progressifs avec difficulté croissante
- Power-ups et bonus multiples
- Système de combo et mode Frenzy
- Zones spéciales (téléportation, bonus, danger)
- Classement des scores (localStorage) - Classement des scores (localStorage)
- Interface responsive - Interface responsive avec support mobile
- Menu principal animé
- Mode plein écran
--- ---
@ -33,25 +39,87 @@ Un jeu de type Pac-Man développé en JavaScript vanilla par **Ludo** et **Syoul
### Gameplay ### Gameplay
- **Déplacement fluide** : Mouvement pixel par pixel avec interpolation - **Déplacement fluide** : Mouvement pixel par pixel avec interpolation
- **Système de vies** : 3 vies représentées par des cœurs - **Système de vies** : 3 vies représentées par des cœurs animés
- **Points** : Collecte de pastilles (+10 points) et bonus - **Points** : Collecte de pastilles avec système de combo
- **Progression de niveau** : Passer au niveau suivant en mangeant 4 cerises - **Progression de niveau** : Passer au niveau suivant en mangeant 4 cerises
- **Mode pause** : Espace ou Échap pour mettre en pause
### Bonus ### Bonus de base
| Bonus | Points | Effet | | Bonus | Points | Effet | Niveau |
|-------|--------|-------| |-------|--------|-------|--------|
| 🍒 Cerise | +100 | Active la poursuite des fantômes pendant un temps limité. 4 cerises = niveau suivant | | 🍒 Cerise | +100 | Rend les fantômes vulnérables. 4 cerises = niveau suivant | 1+ |
| ⭐ Étoile "L" | +15 | Bonus supplémentaire | | ⭐ Étoile "L" | +200 | Bonus supplémentaire | 1+ |
### Power-ups (Niveau 2+)
| Power-up | Effet | Durée | Niveau |
|----------|-------|-------|--------|
| ⚡ Étoile de vitesse | Pacman devient 2x plus rapide | 10 secondes | 2+ |
| 🛡 Bouclier | Protège d'une collision avec un fantôme | 15 secondes | 2+ |
| 💣 Bombe | Repousse tous les fantômes proches | Instantané | 3+ |
| ✨ Multiplicateur | Double tous les points | 30 secondes | 3+ |
### Système de Combo
- 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 plus de 3 secondes entre deux collectes
- Points de base : 10 × combo × Frenzy × multiplicateur
### Mode Frenzy
- S'active automatiquement toutes les **30 secondes**
- Tous les fantômes deviennent vulnérables pendant **10 secondes**
- Score multiplié par **3** pendant la durée du mode
- Indicateur visuel avec animation
### Fantômes ### Fantômes
- **4 fantômes** avec couleurs distinctes (rouge, magenta, cyan, orange)
- **IA adaptative** : Les fantômes poursuivent Pac-Man après qu'une cerise soit mangée #### Types de fantômes
- **Vitesse croissante** : Les fantômes accélèrent à chaque niveau | Type | Description | Niveau |
|------|-------------|--------|
| 👤 Normal | Poursuit directement le joueur | 1+ |
| 🎯 Chasseur | Poursuit agressivement avec prédiction de mouvement | 2+ |
| 🛡 Patrouilleur | Bloque les passages stratégiques et patrouille | 3+ |
| ⚡ Rapide | 1.5x plus rapide que les autres fantômes | 4+ |
| 👻 Invisible | Devient invisible 50% du temps, difficile à voir | 5+ |
#### Nombre de fantômes par niveau
- **Niveau 1-2** : 4 fantômes
- **Niveau 3-4** : 5 fantômes
- **Niveau 5-9** : 6 fantômes
- **Niveau 10+** : 7 fantômes
#### Comportement
- **Poursuite** : Les fantômes poursuivent activement le joueur
- **Vulnérabilité** : Après avoir mangé une cerise, les fantômes deviennent vulnérables et fuient
- **Manger un fantôme vulnérable** : +200 points
- **Prédiction** : Les chasseurs prédisent la position future du joueur
### Zones spéciales (Niveau 4+)
| Zone | Couleur | Effet |
|------|---------|-------|
| Téléportation | Violet | Vous téléporte à un endroit aléatoire |
| Bonus | Doré | Double les points dans cette zone |
| Danger | Rouge | Ralentit Pacman temporairement |
### Labyrinthes ### Labyrinthes
- 3 variantes de labyrinthes qui alternent à chaque niveau - 3 variantes de labyrinthes qui alternent à chaque niveau
- Génération dynamique avec remplissage des espaces vides - Génération dynamique avec remplissage des espaces vides
- Randomisation légère pour plus de variété - Randomisation légère pour plus de variété
- Zones spéciales ajoutées selon le niveau
### Personnalisation
- **8 couleurs** : Jaune, Rouge, Bleu, Vert, Violet, Orange, Rose, Arc-en-ciel (animé)
- **2 formes** : Rond (classique) ou Triangle
- Sauvegarde des préférences dans localStorage
- Aperçu en temps réel dans le menu de personnalisation
### Interface
- **Menu principal** avec animation de fond
- **Classement** en temps réel pendant le jeu
- **Indicateurs visuels** pour les power-ups actifs
- **Compteur de pastilles** restantes
- **Timer de poursuite** quand les fantômes sont vulnérables
- **Mode plein écran** disponible
- **Contrôles tactiles** pour mobile
--- ---
@ -59,9 +127,9 @@ Un jeu de type Pac-Man développé en JavaScript vanilla par **Ludo** et **Syoul
``` ```
pacmanludo/ pacmanludo/
├── index.html # Page HTML principale ├── index.html # Page HTML principale avec menu et interface
├── style.css # Feuille de styles ├── style.css # Feuille de styles complète (1547 lignes)
├── game.js # Logique du jeu ├── game.js # Logique du jeu (2954 lignes)
└── README.md # Documentation └── README.md # Documentation
``` ```
@ -73,32 +141,55 @@ pacmanludo/
2. **Ouvrir** `index.html` dans un navigateur web moderne 2. **Ouvrir** `index.html` dans un navigateur web moderne
3. **Jouer !** 3. **Jouer !**
Aucune dépendance externe n'est requise. Aucune dépendance externe n'est requise. Le jeu fonctionne entièrement côté client.
**Navigateurs supportés :**
- Chrome/Edge (recommandé)
- Firefox
- Safari
- Opéra
--- ---
## 🎯 Comment jouer ## 🎯 Comment jouer
### Contrôles ### Contrôles
#### Clavier
| Touche | Action | | Touche | Action |
|--------|--------| |--------|--------|
| ↑ Flèche Haut | Déplacer vers le haut | | ↑ Flèche Haut | Déplacer vers le haut |
| ↓ Flèche Bas | Déplacer vers le bas | | ↓ Flèche Bas | Déplacer vers le bas |
| ← Flèche Gauche | Déplacer vers la gauche | | ← Flèche Gauche | Déplacer vers la gauche |
| → Flèche Droite | Déplacer vers la droite | | → Flèche Droite | Déplacer vers la droite |
| Espace / Échap | Mettre en pause / Reprendre |
#### Mobile
- Utilisez les boutons directionnels tactiles affichés en bas de l'écran
### Objectif ### Objectif
1. Collectez toutes les pastilles blanches pour marquer des points 1. Collectez toutes les pastilles blanches pour marquer des points
2. Mangez les cerises pour activer la poursuite et progresser 2. Mangez les cerises pour activer la poursuite et progresser
3. Évitez les fantômes ! 3. Collectez les power-ups pour obtenir des avantages
4. Atteignez le score le plus élevé possible 4. Évitez les fantômes (ou mangez-les quand ils sont vulnérables) !
5. Atteignez le score le plus élevé possible
### Progression ### Progression
- **4 cerises mangées** = passage au niveau suivant - **4 cerises mangées** = passage au niveau suivant
- À chaque niveau : - À chaque niveau :
- Pac-Man accélère légèrement (+5% par niveau) - Pac-Man accélère légèrement (+5% par niveau)
- Les fantômes deviennent plus rapides (+20% par niveau) - Les fantômes deviennent plus rapides (vitesse fixe x1.4)
- Le labyrinthe change (cycle de 3 labyrinthes) - Le labyrinthe change (cycle de 3 labyrinthes)
- Nouveaux types de fantômes apparaissent
- De nouveaux power-ups deviennent disponibles
### Système de score
| Action | Points de base | Multiplicateurs |
|--------|----------------|-----------------|
| Pastille | 10 | × combo × Frenzy × multiplicateur |
| Fantôme vulnérable | 200 | × Frenzy × multiplicateur |
| Cerise | 100 | × Frenzy × multiplicateur |
| Étoile Ludo | 200 | × Frenzy × multiplicateur |
--- ---
@ -109,34 +200,52 @@ Aucune dépendance externe n'est requise.
Structure HTML de la page : Structure HTML de la page :
```html ```html
├── main-wrapper ├── main-menu (menu principal)
│ ├── container (zone de jeu) │ ├── menuBackgroundCanvas (animation de fond)
│ ├── Titre "OULVIC" └── menu-container
├── Champ nom d'utilisateur ├── Titre "OULVIC"
├── Barre d'informations (score, niveau, vies, statut) ├── Meilleur score
── Canvas du jeu (600x600) ── Boutons (Jouer, Personnaliser, Classement, Règles)
│ │ └── Instructions et bouton rejouer ├── game-wrapper (interface de jeu)
── leaderboard-container (classement) ── main-wrapper
└── footer (crédits) │ │ ├── container (zone de jeu)
│ │ │ ├── game-header
│ │ │ ├── user-input-section
│ │ │ ├── game-info (score, niveau, vies, pastilles)
│ │ │ ├── power-ups-display
│ │ │ ├── frenzy-indicator
│ │ │ ├── pursuit-indicator
│ │ │ ├── gameCanvas (750x750)
│ │ │ ├── mobile-controls
│ │ │ └── game-overlay (Game Over)
│ │ └── game-leaderboard (classement en temps réel)
│ └── leaderboard-container (classement complet)
├── leaderboard-modal (modal classement)
├── rules-modal (modal règles)
└── customize-modal (modal personnalisation)
``` ```
### `style.css` ### `style.css`
| Section | Description | | Section | Description |
|---------|-------------| |---------|-------------|
| Reset & Body | Styles de base, fond sombre avec effets | | Reset & Body | Styles de base, fond sombre avec effets, curseur personnalisé |
| Animations | Animations CSS (neonFlicker, rainbow, heartbeat, etc.) |
| Menu Principal | Styles du menu avec animation de fond |
| Layout | Flexbox pour disposition responsive | | Layout | Flexbox pour disposition responsive |
| Game Info | Styles pour score, niveau, vies | | Game Info | Styles pour score, niveau, vies, combo |
| Canvas | Bordure dorée, ombres | | Canvas | Bordure dorée, ombres, effets de glow |
| Leaderboard | Tableau des scores stylisé | | Leaderboard | Tableau des scores stylisé avec médailles |
| Responsive | Media queries pour mobile | | Power-ups | Indicateurs visuels pour les power-ups actifs |
| Responsive | Media queries pour mobile et tablette |
| Modals | Styles pour les modales (règles, classement, personnalisation) |
### `game.js` ### `game.js`
#### Constantes principales #### Constantes principales
```javascript ```javascript
CELL_SIZE = 20 // Taille d'une cellule en pixels CELL_SIZE = 25 // Taille d'une cellule en pixels
COLS = 30 // Nombre de colonnes COLS = 30 // Nombre de colonnes
ROWS = 30 // Nombre de lignes ROWS = 30 // Nombre de lignes
@ -147,6 +256,27 @@ EMPTY = 0 // Vide
TUNNEL = 3 // Tunnel (passage fantômes) TUNNEL = 3 // Tunnel (passage fantômes)
BONUS_CHERRY = 4 // Cerise bonus BONUS_CHERRY = 4 // Cerise bonus
BONUS_LUDO = 5 // Étoile bonus BONUS_LUDO = 5 // Étoile bonus
BONUS_SPEED = 6 // Étoile de vitesse
BONUS_SHIELD = 7 // Bouclier
BONUS_BOMB = 8 // Bombe
BONUS_MULTIPLIER = 9 // Multiplicateur
// Zones spéciales
ZONE_TELEPORT = 10 // Zone de téléportation
ZONE_BONUS = 11 // Zone bonus
ZONE_DANGER = 12 // Zone danger
// Types de fantômes
GHOST_NORMAL = 'normal'
GHOST_HUNTER = 'hunter'
GHOST_PATROL = 'patrol'
GHOST_FAST = 'fast'
GHOST_INVISIBLE = 'invisible'
// Constantes de gameplay
COMBO_TIMEOUT = 3000 // 3 secondes pour maintenir le combo
FRENZY_INTERVAL = 30000 // 30 secondes entre chaque mode Frenzy
FRENZY_DURATION = 10000 // 10 secondes de durée du mode Frenzy
``` ```
--- ---
@ -157,62 +287,111 @@ BONUS_LUDO = 5 // Étoile bonus
Gère le personnage jouable. Gère le personnage jouable.
#### Propriétés
| Propriété | Type | Description | | Propriété | Type | Description |
|-----------|------|-------------| |-----------|------|-------------|
| `x, y` | number | Position en cellules | | `x, y` | number | Position en cellules |
| `pixelX, pixelY` | number | Position en pixels (mouvement fluide) | | `pixelX, pixelY` | number | Position en pixels (mouvement fluide) |
| `direction` | number | Direction actuelle (0=haut, 1=droite, 2=bas, 3=gauche) | | `direction` | number | Direction actuelle (0=haut, 1=droite, 2=bas, 3=gauche) |
| `nextDirection` | number | Prochaine direction demandée | | `nextDirection` | number | Prochaine direction demandée |
| `speed` | number | Vitesse de déplacement | | `speed` | number | Vitesse de déplacement (modifiable par power-ups) |
| `baseSpeed` | number | Vitesse de base (0.25) |
| `mouthAngle` | number | Animation de la bouche | | `mouthAngle` | number | Animation de la bouche |
| `colorAnimation` | number | Animation des couleurs HSL | | `mouthOpen` | boolean | État de la bouche (ouverte/fermée) |
| `colorAnimation` | number | Animation des couleurs HSL (pour arc-en-ciel) |
| `color` | string | Couleur du joueur (sauvegardée) |
| `shape` | string | Forme du joueur ('round' ou 'triangle') |
#### Méthodes
| Méthode | Description | | Méthode | Description |
|---------|-------------| |---------|-------------|
| `update()` | Met à jour la position et l'animation | | `update()` | Met à jour la position, l'animation et collecte les objets |
| `canMove(direction)` | Vérifie si le mouvement est possible | | `canMove(direction)` | Vérifie si le mouvement est possible dans une direction |
| `collectDot()` | Collecte les pastilles et bonus | | `collectDot()` | Collecte les pastilles, bonus et active les power-ups |
| `draw()` | Dessine Pac-Man sur le canvas | | `draw()` | Dessine Pac-Man sur le canvas avec la couleur et forme choisies |
--- ---
### Classe `Ghost` ### Classe `Ghost`
Gère les fantômes ennemis. Gère les fantômes ennemis avec IA avancée.
#### Propriétés
| Propriété | Type | Description | | Propriété | Type | Description |
|-----------|------|-------------| |-----------|------|-------------|
| `x, y` | number | Position en cellules | | `x, y` | number | Position en cellules |
| `pixelX, pixelY` | number | Position en pixels | | `pixelX, pixelY` | number | Position en pixels |
| `color` | string | Couleur du fantôme | | `color` | string | Couleur du fantôme |
| `type` | string | Type de fantôme (normal, hunter, patrol, fast, invisible) |
| `direction` | number | Direction de déplacement | | `direction` | number | Direction de déplacement |
| `speed` | number | Vitesse (augmente avec le niveau) | | `speed` | number | Vitesse (ajustée selon le type et le niveau) |
| `baseSpeed` | number | Vitesse de base (0.15) |
| `moveCounter` | number | Compteur pour les changements de direction |
| `moveInterval` | number | Intervalle entre changements de direction | | `moveInterval` | number | Intervalle entre changements de direction |
| `isVulnerable` | boolean | État de vulnérabilité |
| `vulnerableTimer` | number | Durée restante de vulnérabilité |
| `isInvisible` | boolean | État d'invisibilité (pour GHOST_INVISIBLE) |
| `invisibleTimer` | number | Timer pour l'invisibilité |
| `patrolTarget` | object | Cible de patrouille (pour GHOST_PATROL) |
| `patrolIndex` | number | Index de patrouille |
#### Méthodes
| Méthode | Description | | Méthode | Description |
|---------|-------------| |---------|-------------|
| `update()` | Met à jour la position et l'IA | | `update()` | Met à jour la position, l'IA et les états spéciaux |
| `updateSpeed()` | Ajuste la vitesse selon le niveau | | `updateSpeed()` | Ajuste la vitesse selon le type et le niveau |
| `canMove(direction)` | Vérifie si le mouvement est possible | | `canMove(direction)` | Vérifie si le mouvement est possible |
| `getDirectionToPacman(dirs)` | Calcule la meilleure direction vers Pac-Man | | `getDirectionToPacman(dirs)` | Calcule la meilleure direction vers Pac-Man avec prédiction |
| `draw()` | Dessine le fantôme (corps + yeux) | | `getDirectionAwayFromPacman(dirs)` | Calcule la direction pour fuir Pac-Man (quand vulnérable) |
| `getPatrolDirection(dirs)` | Calcule la direction de patrouille (pour GHOST_PATROL) |
| `draw()` | Dessine le fantôme (corps + yeux, avec effets spéciaux) |
--- ---
### Classe `Bonus` ### Classe `Bonus`
Gère les objets bonus (cerises, étoiles). Gère les objets bonus (cerises, étoiles, power-ups).
#### Propriétés
| Propriété | Type | Description | | Propriété | Type | Description |
|-----------|------|-------------| |-----------|------|-------------|
| `x, y` | number | Position en cellules | | `x, y` | number | Position en cellules |
| `type` | number | Type de bonus (CHERRY ou LUDO) | | `type` | number | Type de bonus (BONUS_CHERRY, BONUS_LUDO, etc.) |
| `animation` | number | Animation de pulsation | | `animation` | number | Animation de pulsation |
#### Méthodes
| Méthode | Description | | Méthode | Description |
|---------|-------------| |---------|-------------|
| `update()` | Met à jour l'animation | | `update()` | Met à jour l'animation |
| `draw()` | Dessine le bonus avec effet de scale | | `draw()` | Dessine le bonus avec effet de scale et animation |
---
### Classe `MenuGhost`
Gère les fantômes animés dans le menu principal.
#### Propriétés
| Propriété | Type | Description |
|-----------|------|-------------|
| `x, y` | number | Position en pixels |
| `vx, vy` | number | Vitesse en pixels |
| `color` | string | Couleur du fantôme |
| `size` | number | Taille du fantôme |
#### Méthodes
| Méthode | Description |
|---------|-------------|
| `update()` | Met à jour la position |
| `draw(ctx)` | Dessine le fantôme sur le canvas du menu |
--- ---
@ -223,10 +402,10 @@ Gère les objets bonus (cerises, étoiles).
| Fonction | Description | | Fonction | Description |
|----------|-------------| |----------|-------------|
| `countDots()` | Compte le nombre de pastilles restantes | | `countDots()` | Compte le nombre de pastilles restantes |
| `drawMaze()` | Dessine le labyrinthe complet | | `drawMaze()` | Dessine le labyrinthe complet avec zones spéciales |
| `fillEmptySpaces()` | Remplit les espaces vides avec des pastilles | | `fillEmptySpaces()` | Remplit les espaces vides avec des pastilles |
| `randomizeMaze()` | Ajoute des variations aléatoires au labyrinthe | | `randomizeMaze()` | Ajoute des variations aléatoires au labyrinthe |
| `placeBonuses()` | Place les bonus aux positions définies | | `placeBonuses()` | Place les bonus aux positions définies selon le niveau |
#### Gestion du jeu #### Gestion du jeu
@ -235,17 +414,52 @@ Gère les objets bonus (cerises, étoiles).
| `initGame()` | Initialise une nouvelle partie | | `initGame()` | Initialise une nouvelle partie |
| `gameLoop()` | Boucle principale du jeu (requestAnimationFrame) | | `gameLoop()` | Boucle principale du jeu (requestAnimationFrame) |
| `checkCollisions()` | Détecte les collisions Pac-Man/fantômes | | `checkCollisions()` | Détecte les collisions Pac-Man/fantômes |
| `nextLevel()` | Passe au niveau suivant | | `nextLevel()` | Passe au niveau suivant avec transition |
| `restartCurrentLevel()` | Recommence le niveau actuel (après perte de vie) | | `restartCurrentLevel()` | Recommence le niveau actuel (après perte de vie) |
| `createGhosts()` | Crée les fantômes selon le niveau et leurs types |
#### Interface utilisateur #### Interface utilisateur
| Fonction | Description | | Fonction | Description |
|----------|-------------| |----------|-------------|
| `updateLivesDisplay()` | Met à jour l'affichage des vies | | `updateLivesDisplay()` | Met à jour l'affichage des vies |
| `updateLeaderboard()` | Rafraîchit le classement | | `updateLeaderboard()` | Rafraîchit le classement principal |
| `updateGameLeaderboard()` | Rafraîchit le classement en jeu |
| `updateMenuLeaderboard()` | Rafraîchit le classement du menu |
| `saveScore()` | Sauvegarde le score en localStorage | | `saveScore()` | Sauvegarde le score en localStorage |
| `getScores()` | Récupère les scores depuis localStorage | | `getScores()` | Récupère les scores depuis localStorage |
| `showGameOver()` | Affiche l'overlay Game Over |
| `hideGameOver()` | Cache l'overlay Game Over |
| `confirmUsername()` | Confirme le nom d'utilisateur |
| `resetUsernameInput()` | Réinitialise le champ nom d'utilisateur |
#### Personnalisation
| Fonction | Description |
|----------|-------------|
| `updateColorSelection()` | Met à jour la sélection de couleur |
| `updateShapeSelection()` | Met à jour la sélection de forme |
| `updatePlayerPreview()` | Met à jour l'aperçu du joueur |
| `startPreviewAnimation()` | Démarre l'animation de l'aperçu |
| `stopPreviewAnimation()` | Arrête l'animation de l'aperçu |
#### Menu et animations
| Fonction | Description |
|----------|-------------|
| `animateMenu()` | Anime le menu principal |
| `startMenuAnimation()` | Démarre l'animation du menu |
| `stopMenuAnimation()` | Arrête l'animation du menu |
| `resizeMenuCanvas()` | Redimensionne le canvas du menu |
| `updateBestScore()` | Met à jour l'affichage du meilleur score |
#### Plein écran
| Fonction | Description |
|----------|-------------|
| `toggleFullscreen()` | Active/désactive le mode plein écran |
| `updateFullscreenButton()` | Met à jour le bouton plein écran |
| `isFullscreen()` | Vérifie si on est en mode plein écran |
--- ---
@ -256,15 +470,40 @@ let score = 0; // Score actuel
let level = 1; // Niveau actuel let level = 1; // Niveau actuel
let lives = 3; // Vies restantes let lives = 3; // Vies restantes
let gameRunning = true; // État du jeu let gameRunning = true; // État du jeu
let isPaused = false; // État de pause
let totalDots = 0; // Pastilles restantes let totalDots = 0; // Pastilles restantes
let cherriesEaten = 0; // Cerises mangées (niveau actuel) let cherriesEaten = 0; // Cerises mangées (niveau actuel)
let isChangingLevel = false; // Flag de transition let isChangingLevel = false; // Flag de transition
let cherryEatenRecently = false; // Mode poursuite activé let cherryEatenRecently = false; // Mode poursuite activé
let cherryEatenTimer = 0; // Durée du mode poursuite let cherryEatenTimer = 0; // Durée du mode poursuite
// Système de combo
let comboCount = 0; // Nombre de pastilles dans le combo
let comboMultiplier = 1; // Multiplicateur actuel (max x5)
let lastDotTime = 0; // Temps de la dernière pastille collectée
// Power-ups actifs
let speedBoostActive = false; // État du boost de vitesse
let speedBoostTimer = 0; // Durée restante
let shieldActive = false; // État du bouclier
let shieldTimer = 0; // Durée restante
let scoreMultiplierActive = false; // État du multiplicateur
let scoreMultiplierTimer = 0; // Durée restante
let scoreMultiplierValue = 1; // Valeur du multiplicateur (x2)
// Mode Frenzy
let frenzyModeActive = false; // État du mode Frenzy
let frenzyTimer = 0; // Durée restante
let frenzyCooldown = 0; // Cooldown avant le prochain Frenzy
// Zones spéciales
let specialZones = []; // Zones spéciales actives
``` ```
--- ---
## 🔧 Systèmes avancés
### Système de stockage des scores ### Système de stockage des scores
Les scores sont sauvegardés dans le `localStorage` du navigateur sous la clé `pacmanScores`. Les scores sont sauvegardés dans le `localStorage` du navigateur sous la clé `pacmanScores`.
@ -274,14 +513,14 @@ Les scores sont sauvegardés dans le `localStorage` du navigateur sous la clé `
{ {
username: "NomJoueur", // Nom saisi ou "Anonyme" username: "NomJoueur", // Nom saisi ou "Anonyme"
score: 1500, // Score final score: 1500, // Score final
level: 5, // Niveau atteint
date: "2025-12-01T..." // Date ISO date: "2025-12-01T..." // Date ISO
} }
``` ```
- Maximum **10 scores** conservés - Maximum **10 scores** conservés
- Triés par score décroissant - Triés par score décroissant
- Affichage avec médailles pour le top 3 (🥇 🥈 🥉)
---
### Labyrinthes ### Labyrinthes
@ -293,8 +532,45 @@ mazeIndex = (level - 1) % 3
``` ```
Les labyrinthes sont modifiés dynamiquement : Les labyrinthes sont modifiés dynamiquement :
1. `fillEmptySpaces()` - Remplit les zones vides 1. `fillEmptySpaces()` - Remplit les zones vides avec des pastilles
2. `randomizeMaze()` - Ajoute de la variété 2. `randomizeMaze()` - Ajoute de la variété en modifiant certains murs
3. `placeBonuses()` - Place les bonus et zones spéciales selon le niveau
### Système de fruits
Les fruits changent selon le niveau :
- Niveau 1 : Cerise rouge
- Niveau 2 : Banane
- Niveau 3 : Orange
- Niveau 4 : Pomme
- Niveau 5 : Raisin
- Niveau 6 : Fraise
- Niveau 7+ : Ananas (rotation)
### IA des fantômes
#### Fantôme Normal
- Poursuit directement le joueur
- Changement de direction périodique
#### Chasseur (Niveau 2+)
- Prédit la position future du joueur (3-4 cases d'avance)
- Poursuite plus agressive
- Pas de restriction sur le retour en arrière
#### Patrouilleur (Niveau 3+)
- Si proche du joueur (< 12 cases) : poursuit
- Si loin : patrouille vers les zones centrales
- Bloque les passages stratégiques
#### Rapide (Niveau 4+)
- Vitesse 1.5x supérieure aux autres
- Même comportement de poursuite que Normal
#### Invisible (Niveau 5+)
- Devient invisible 50% du temps
- Timer alternant toutes les 5 secondes
- Difficile à voir et à éviter
--- ---
@ -307,18 +583,34 @@ Les labyrinthes sont modifiés dynamiquement :
| Fond | `#0a0a0a` (noir) | | Fond | `#0a0a0a` (noir) |
| Murs | `#0000ff` (bleu) | | Murs | `#0000ff` (bleu) |
| Pastilles | `#ffffff` (blanc) | | Pastilles | `#ffffff` (blanc) |
| Texte titre | `#ffd700` (or) | | Texte titre | `#ffd700` (or) avec animation arc-en-ciel |
| Cerises | `#ff0000` (rouge) | | Cerises | `#ff0000` (rouge) |
| Cœurs | `#ff0000` (rouge) | | Cœurs | `#ff0000` (rouge) avec animation heartbeat |
| Zones téléportation | `rgba(255, 0, 255, 0.3)` (violet) |
| Zones bonus | `rgba(255, 215, 0, 0.3)` (doré) |
| Zones danger | `rgba(255, 0, 0, 0.3)` (rouge) |
### Fantômes ### Fantômes
- Rouge : `#ff0000` - Rouge : `#ff0000`
- Magenta : `#ff00ff` - Magenta : `#ff00ff`
- Cyan : `#00ffff` - Cyan : `#00ffff`
- Orange : `#ffa500` - Orange : `#ffa500`
- Vert : `#00ff00`
- Jaune : `#ffff00`
### Pac-Man ### Pac-Man
- Couleur animée en HSL (arc-en-ciel) - Couleur animée en HSL (arc-en-ciel) par défaut
- 8 couleurs disponibles : Jaune, Rouge, Bleu, Vert, Violet, Orange, Rose, Arc-en-ciel
- 2 formes : Rond (classique) ou Triangle
### Animations CSS
- `neonFlicker` : Effet de scintillement néon pour le titre
- `rainbow` : Animation arc-en-ciel pour le texte
- `heartbeat` : Animation de pulsation pour les cœurs
- `canvasGlow` : Effet de lueur pour le canvas
- `pulse` : Animation de pulsation pour les indicateurs
- `fadeIn` : Animation d'apparition
- `shake` : Animation de secousse pour Game Over
--- ---
@ -329,30 +621,78 @@ Les labyrinthes sont modifiés dynamiquement :
Dans `game.js` : Dans `game.js` :
```javascript ```javascript
// Vitesse de base de Pac-Man (ligne 145) // Vitesse de base de Pac-Man (ligne ~265)
this.baseSpeed = 0.25;
// Vitesse de base des fantômes (ligne ~575)
this.baseSpeed = 0.15; this.baseSpeed = 0.15;
// Vitesse de base des fantômes (ligne 277) // Accélération par niveau (ligne ~1504)
this.baseSpeed = 0.1;
// Accélération par niveau
pacman.speed = pacman.baseSpeed * (1 + (level - 1) * 0.05); pacman.speed = pacman.baseSpeed * (1 + (level - 1) * 0.05);
ghost.speed = ghost.baseSpeed * (1 + (level - 1) * 0.2);
``` ```
### Modifier les points ### Modifier les points
```javascript ```javascript
// Pastille (ligne 213) // Pastille (ligne ~357)
score += 10; let points = 10 * comboMultiplier * (frenzyModeActive ? 3 : 1) * scoreMultiplierValue;
// Cerise (ligne 224) // Cerise (ligne ~372)
score += 100; score += 100;
// Étoile Ludo (ligne 241) // Étoile Ludo (ligne ~401)
score += 15; let points = 200 * (frenzyModeActive ? 3 : 1) * scoreMultiplierValue;
// Fantôme vulnérable (ligne ~1298)
score += 200;
``` ```
### Modifier les durées
```javascript
// Combo timeout (ligne ~230)
const COMBO_TIMEOUT = 3000; // 3 secondes
// Mode Frenzy (lignes ~245-246)
const FRENZY_INTERVAL = 30000; // 30 secondes
const FRENZY_DURATION = 10000; // 10 secondes
// Power-ups (lignes ~408, 415, 436)
speedBoostTimer = 600; // 10 secondes à 60 FPS
shieldTimer = 900; // 15 secondes
scoreMultiplierTimer = 1800; // 30 secondes
```
---
## 📱 Responsive Design
Le jeu est entièrement responsive avec :
- **Desktop** : Interface complète avec classement à droite
- **Tablette** : Layout adapté, classement en dessous
- **Mobile** : Contrôles tactiles, layout vertical, classement en dessous
Media queries :
- `@media (max-width: 1200px)` : Layout vertical
- `@media (max-width: 700px)` : Optimisations mobile
---
## 🐛 Dépannage
### Le jeu ne démarre pas
- Vérifiez que vous utilisez un navigateur moderne
- Ouvrez la console (F12) pour voir les erreurs
- Vérifiez que tous les fichiers sont présents
### Les scores ne se sauvegardent pas
- Vérifiez que le localStorage est activé dans votre navigateur
- Videz le cache si nécessaire
### Le mode plein écran ne fonctionne pas
- Certains navigateurs nécessitent une interaction utilisateur
- Essayez de cliquer sur le bouton après le chargement de la page
--- ---
## 📄 Licence ## 📄 Licence
@ -365,5 +705,26 @@ Projet créé par **Ludo** et **Syoul**.
- Inspiration : Pac-Man original (Namco, 1980) - Inspiration : Pac-Man original (Namco, 1980)
- Développement : Ludo & Syoul - Développement : Ludo & Syoul
- Police : Press Start 2P (Google Fonts)
---
## 🔄 Changelog
### Version actuelle
- Système de combo
- Mode Frenzy automatique
- Power-ups multiples (vitesse, bouclier, bombe, multiplicateur)
- Types de fantômes variés avec IA avancée
- Zones spéciales (téléportation, bonus, danger)
- Personnalisation complète du joueur
- Menu principal animé
- Classement en temps réel
- Mode plein écran
- Support mobile avec contrôles tactiles
- Système de pause
- Fruits variés selon le niveau
---
**Amusez-vous bien avec OULVIC ! 🎮👻**

301
game.js
View File

@ -550,6 +550,124 @@ class Pacman {
// Restaurer la couleur originale // Restaurer la couleur originale
ctx.fillStyle = baseFillColor; ctx.fillStyle = baseFillColor;
} else if (this.shape === 'zebre') {
// Dessiner un zèbre plus réaliste
const bodyWidth = size * 1.8;
const bodyHeight = size * 1.4;
ctx.save();
// Corps (ellipse allongée)
ctx.beginPath();
ctx.ellipse(0, size * 0.2, bodyWidth, bodyHeight, 0, 0, Math.PI * 2);
ctx.fillStyle = '#ffffff';
ctx.fill();
ctx.strokeStyle = '#000000';
ctx.lineWidth = 2;
ctx.stroke();
// Rayures noires horizontales sur le corps
ctx.fillStyle = '#000000';
const stripeCount = 8;
const stripeSpacing = (bodyHeight * 2) / stripeCount;
for (let i = 0; i < stripeCount; i++) {
if (i % 2 === 1) { // Alterner les rayures
const yPos = -bodyHeight + (i * stripeSpacing);
ctx.beginPath();
ctx.ellipse(0, yPos + size * 0.2, bodyWidth * 0.95, stripeSpacing * 0.5, 0, 0, Math.PI * 2);
ctx.fill();
}
}
// Tête (cercle légèrement plus petit au-dessus du corps)
const headSize = size * 0.9;
ctx.beginPath();
if (this.mouthOpen) {
ctx.arc(0, -bodyHeight - headSize * 0.2, headSize, 0.15, Math.PI * 2 - 0.15);
ctx.lineTo(0, -bodyHeight - headSize * 0.2);
ctx.closePath();
} else {
ctx.arc(0, -bodyHeight - headSize * 0.2, headSize, 0, Math.PI * 2);
}
ctx.fillStyle = '#ffffff';
ctx.fill();
ctx.strokeStyle = '#000000';
ctx.lineWidth = 2;
ctx.stroke();
// Rayures sur la tête (verticales)
ctx.fillStyle = '#000000';
ctx.fillRect(-headSize * 0.4, -bodyHeight - headSize * 0.5, headSize * 0.15, headSize * 0.6);
ctx.fillRect(headSize * 0.25, -bodyHeight - headSize * 0.5, headSize * 0.15, headSize * 0.6);
// Oreilles pointues
ctx.beginPath();
// Oreille gauche
ctx.moveTo(-headSize * 0.6, -bodyHeight - headSize * 0.8);
ctx.lineTo(-headSize * 0.8, -bodyHeight - headSize * 1.4);
ctx.lineTo(-headSize * 0.4, -bodyHeight - headSize * 0.9);
ctx.closePath();
ctx.fillStyle = '#ffffff';
ctx.fill();
ctx.stroke();
// Rayure sur l'oreille gauche
ctx.fillStyle = '#000000';
ctx.beginPath();
ctx.moveTo(-headSize * 0.65, -bodyHeight - headSize * 0.95);
ctx.lineTo(-headSize * 0.75, -bodyHeight - headSize * 1.3);
ctx.lineTo(-headSize * 0.55, -bodyHeight - headSize * 1.0);
ctx.closePath();
ctx.fill();
// Oreille droite
ctx.fillStyle = '#ffffff';
ctx.beginPath();
ctx.moveTo(headSize * 0.6, -bodyHeight - headSize * 0.8);
ctx.lineTo(headSize * 0.8, -bodyHeight - headSize * 1.4);
ctx.lineTo(headSize * 0.4, -bodyHeight - headSize * 0.9);
ctx.closePath();
ctx.fill();
ctx.stroke();
// Rayure sur l'oreille droite
ctx.fillStyle = '#000000';
ctx.beginPath();
ctx.moveTo(headSize * 0.65, -bodyHeight - headSize * 0.95);
ctx.lineTo(headSize * 0.75, -bodyHeight - headSize * 1.3);
ctx.lineTo(headSize * 0.55, -bodyHeight - headSize * 1.0);
ctx.closePath();
ctx.fill();
// Yeux
ctx.fillStyle = '#ffffff';
ctx.beginPath();
ctx.arc(-headSize * 0.3, -bodyHeight - headSize * 0.3, headSize * 0.15, 0, Math.PI * 2);
ctx.arc(headSize * 0.3, -bodyHeight - headSize * 0.3, headSize * 0.15, 0, Math.PI * 2);
ctx.fill();
// Pupilles
ctx.fillStyle = '#000000';
ctx.beginPath();
ctx.arc(-headSize * 0.3, -bodyHeight - headSize * 0.3, headSize * 0.08, 0, Math.PI * 2);
ctx.arc(headSize * 0.3, -bodyHeight - headSize * 0.3, headSize * 0.08, 0, Math.PI * 2);
ctx.fill();
// Reflets dans les yeux
ctx.fillStyle = '#ffffff';
ctx.beginPath();
ctx.arc(-headSize * 0.32, -bodyHeight - headSize * 0.32, headSize * 0.03, 0, Math.PI * 2);
ctx.arc(headSize * 0.28, -bodyHeight - headSize * 0.32, headSize * 0.03, 0, Math.PI * 2);
ctx.fill();
// Queue (petite touffe)
ctx.fillStyle = '#000000';
ctx.beginPath();
ctx.arc(bodyWidth * 0.6, bodyHeight * 0.6, size * 0.2, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
} else { } else {
// Forme ronde (par défaut) // Forme ronde (par défaut)
if (this.mouthOpen) { if (this.mouthOpen) {
@ -2339,9 +2457,25 @@ class MenuGhost {
this.size = 30 + Math.random() * 20; this.size = 30 + Math.random() * 20;
this.color = ['#ff0000', '#ff00ff', '#00ffff', '#ffa500'][Math.floor(Math.random() * 4)]; this.color = ['#ff0000', '#ff00ff', '#00ffff', '#ffa500'][Math.floor(Math.random() * 4)];
this.animation = Math.random() * Math.PI * 2; this.animation = Math.random() * Math.PI * 2;
this.isDestroyed = false;
this.destroyAnimation = 0;
}
// Vérifier si le curseur touche le fantôme
isPointInside(mouseX, mouseY) {
const distance = Math.sqrt(
Math.pow(mouseX - this.x, 2) +
Math.pow(mouseY - this.y, 2)
);
return distance < this.size * 0.7;
} }
update() { update() {
if (this.isDestroyed) {
this.destroyAnimation += 0.1;
return;
}
this.x += this.speedX; this.x += this.speedX;
this.y += this.speedY; this.y += this.speedY;
this.animation += 0.05; this.animation += 0.05;
@ -2360,6 +2494,33 @@ class MenuGhost {
} }
draw() { draw() {
if (this.isDestroyed) {
// Animation de destruction (particules qui s'éparpillent)
if (this.destroyAnimation > 2) {
return;
}
menuCtx.save();
menuCtx.globalAlpha = 1 - (this.destroyAnimation / 2);
// Dessiner des particules qui s'éparpillent
const particleCount = 8;
for (let i = 0; i < particleCount; i++) {
const angle = (Math.PI * 2 / particleCount) * i + this.destroyAnimation * 2;
const distance = this.destroyAnimation * 30;
const particleX = this.x + Math.cos(angle) * distance;
const particleY = this.y + Math.sin(angle) * distance;
menuCtx.fillStyle = this.color;
menuCtx.beginPath();
menuCtx.arc(particleX, particleY, 4, 0, Math.PI * 2);
menuCtx.fill();
}
menuCtx.restore();
return;
}
menuCtx.save(); menuCtx.save();
menuCtx.translate(this.x, this.y); menuCtx.translate(this.x, this.y);
@ -2440,6 +2601,13 @@ function animateMenu() {
ghost.draw(); ghost.draw();
}); });
// Remplacer les fantômes complètement détruits
for (let i = 0; i < menuGhosts.length; i++) {
if (menuGhosts[i].isDestroyed && menuGhosts[i].destroyAnimation > 2) {
menuGhosts[i] = new MenuGhost();
}
}
menuAnimationFrame = requestAnimationFrame(animateMenu); menuAnimationFrame = requestAnimationFrame(animateMenu);
} }
@ -2488,6 +2656,25 @@ if (mainMenu) {
menuObserver.observe(mainMenu, { attributes: true, attributeFilter: ['style'] }); menuObserver.observe(mainMenu, { attributes: true, attributeFilter: ['style'] });
} }
// Gestion de la destruction des fantômes avec le curseur
let mouseX = 0;
let mouseY = 0;
menuCanvas.addEventListener('mousemove', (e) => {
const rect = menuCanvas.getBoundingClientRect();
mouseX = e.clientX - rect.left;
mouseY = e.clientY - rect.top;
// Vérifier les collisions avec les fantômes
menuGhosts.forEach(ghost => {
if (!ghost.isDestroyed && ghost.isPointInside(mouseX, mouseY)) {
// Détruire le fantôme
ghost.isDestroyed = true;
ghost.destroyAnimation = 0;
}
});
});
// === GESTION DU MENU === // === GESTION DU MENU ===
// Afficher le jeu // Afficher le jeu
playBtn.addEventListener('click', () => { playBtn.addEventListener('click', () => {
@ -2748,6 +2935,120 @@ function startPreviewAnimation() {
}; };
ctx.fillStyle = colorMap[selectedColor] || '#ffd700'; ctx.fillStyle = colorMap[selectedColor] || '#ffd700';
} }
} else if (selectedShape === 'zebre') {
// Dessiner un zèbre plus réaliste
const bodyWidth = size * 1.8;
const bodyHeight = size * 1.4;
// Corps (ellipse allongée)
ctx.beginPath();
ctx.ellipse(0, size * 0.2, bodyWidth, bodyHeight, 0, 0, Math.PI * 2);
ctx.fillStyle = '#ffffff';
ctx.fill();
ctx.strokeStyle = '#000000';
ctx.lineWidth = 2;
ctx.stroke();
// Rayures noires horizontales sur le corps
ctx.fillStyle = '#000000';
const stripeCount = 8;
const stripeSpacing = (bodyHeight * 2) / stripeCount;
for (let i = 0; i < stripeCount; i++) {
if (i % 2 === 1) { // Alterner les rayures
const yPos = -bodyHeight + (i * stripeSpacing);
ctx.beginPath();
ctx.ellipse(0, yPos + size * 0.2, bodyWidth * 0.95, stripeSpacing * 0.5, 0, 0, Math.PI * 2);
ctx.fill();
}
}
// Tête (cercle légèrement plus petit au-dessus du corps)
const headSize = size * 0.9;
ctx.beginPath();
if (mouthOpen) {
ctx.arc(0, -bodyHeight - headSize * 0.2, headSize, 0.15, Math.PI * 2 - 0.15);
ctx.lineTo(0, -bodyHeight - headSize * 0.2);
ctx.closePath();
} else {
ctx.arc(0, -bodyHeight - headSize * 0.2, headSize, 0, Math.PI * 2);
}
ctx.fillStyle = '#ffffff';
ctx.fill();
ctx.strokeStyle = '#000000';
ctx.lineWidth = 2;
ctx.stroke();
// Rayures sur la tête (verticales)
ctx.fillStyle = '#000000';
ctx.fillRect(-headSize * 0.4, -bodyHeight - headSize * 0.5, headSize * 0.15, headSize * 0.6);
ctx.fillRect(headSize * 0.25, -bodyHeight - headSize * 0.5, headSize * 0.15, headSize * 0.6);
// Oreilles pointues
ctx.beginPath();
// Oreille gauche
ctx.moveTo(-headSize * 0.6, -bodyHeight - headSize * 0.8);
ctx.lineTo(-headSize * 0.8, -bodyHeight - headSize * 1.4);
ctx.lineTo(-headSize * 0.4, -bodyHeight - headSize * 0.9);
ctx.closePath();
ctx.fillStyle = '#ffffff';
ctx.fill();
ctx.stroke();
// Rayure sur l'oreille gauche
ctx.fillStyle = '#000000';
ctx.beginPath();
ctx.moveTo(-headSize * 0.65, -bodyHeight - headSize * 0.95);
ctx.lineTo(-headSize * 0.75, -bodyHeight - headSize * 1.3);
ctx.lineTo(-headSize * 0.55, -bodyHeight - headSize * 1.0);
ctx.closePath();
ctx.fill();
// Oreille droite
ctx.fillStyle = '#ffffff';
ctx.beginPath();
ctx.moveTo(headSize * 0.6, -bodyHeight - headSize * 0.8);
ctx.lineTo(headSize * 0.8, -bodyHeight - headSize * 1.4);
ctx.lineTo(headSize * 0.4, -bodyHeight - headSize * 0.9);
ctx.closePath();
ctx.fill();
ctx.stroke();
// Rayure sur l'oreille droite
ctx.fillStyle = '#000000';
ctx.beginPath();
ctx.moveTo(headSize * 0.65, -bodyHeight - headSize * 0.95);
ctx.lineTo(headSize * 0.75, -bodyHeight - headSize * 1.3);
ctx.lineTo(headSize * 0.55, -bodyHeight - headSize * 1.0);
ctx.closePath();
ctx.fill();
// Yeux
ctx.fillStyle = '#ffffff';
ctx.beginPath();
ctx.arc(-headSize * 0.3, -bodyHeight - headSize * 0.3, headSize * 0.15, 0, Math.PI * 2);
ctx.arc(headSize * 0.3, -bodyHeight - headSize * 0.3, headSize * 0.15, 0, Math.PI * 2);
ctx.fill();
// Pupilles
ctx.fillStyle = '#000000';
ctx.beginPath();
ctx.arc(-headSize * 0.3, -bodyHeight - headSize * 0.3, headSize * 0.08, 0, Math.PI * 2);
ctx.arc(headSize * 0.3, -bodyHeight - headSize * 0.3, headSize * 0.08, 0, Math.PI * 2);
ctx.fill();
// Reflets dans les yeux
ctx.fillStyle = '#ffffff';
ctx.beginPath();
ctx.arc(-headSize * 0.32, -bodyHeight - headSize * 0.32, headSize * 0.03, 0, Math.PI * 2);
ctx.arc(headSize * 0.28, -bodyHeight - headSize * 0.32, headSize * 0.03, 0, Math.PI * 2);
ctx.fill();
// Queue (petite touffe)
ctx.fillStyle = '#000000';
ctx.beginPath();
ctx.arc(bodyWidth * 0.6, bodyHeight * 0.6, size * 0.2, 0, Math.PI * 2);
ctx.fill();
} else { } else {
// Dessiner un cercle // Dessiner un cercle
if (mouthOpen) { if (mouthOpen) {

View File

@ -287,6 +287,10 @@
<div class="shape-preview triangle-preview"></div> <div class="shape-preview triangle-preview"></div>
<span>Triangle</span> <span>Triangle</span>
</div> </div>
<div class="shape-option" data-shape="zebre" data-name="Zèbre">
<div class="shape-preview zebre-preview"></div>
<span>Zèbre</span>
</div>
</div> </div>
</div> </div>
<div class="customize-preview"> <div class="customize-preview">

View File

@ -156,7 +156,7 @@ body::after {
height: 100%; height: 100%;
z-index: 1; z-index: 1;
opacity: 0.3; opacity: 0.3;
pointer-events: none; pointer-events: auto;
} }
.menu-container { .menu-container {
@ -1539,6 +1539,21 @@ footer {
filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.3)); filter: drop-shadow(0 0 5px rgba(255, 255, 255, 0.3));
} }
.zebre-preview {
width: 50px;
height: 50px;
border-radius: 50%;
background: repeating-linear-gradient(
90deg,
#ffffff 0px,
#ffffff 8px,
#000000 8px,
#000000 16px
);
border: 3px solid #fff;
box-shadow: 0 0 10px rgba(255, 255, 255, 0.3);
}
.shape-option span { .shape-option span {
font-size: 0.7em; font-size: 0.7em;
color: #fff; color: #fff;