diff --git a/Transcendence/README.md b/Transcendence/README.md deleted file mode 100644 index 0878b16..0000000 --- a/Transcendence/README.md +++ /dev/null @@ -1,348 +0,0 @@ -# Transcendence - ---- - -## Configuration — Fichier `.env` - -```env -POSTGRES_PASSWORD=coucou -JWT_SECRET=superlongsecretkeyatleast32characterspleasenevercommitthis -POSTGRES_DB=database -POSTGRES_HOST=database -POSTGRES_USER=user - -GITHUB_CLIENT_ID=Iv1.xxxxxxxxxxxxxxxxxxxxxxxxxxx -GITHUB_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -GITHUB_CALLBACK_URL=http://localhost:8080/api/auth/github/callback -``` - -> Les variables `GITHUB_*` sont à générer sur [github.com/settings/applications/new](https://github.com/settings/applications/new) - ---- - -## Gestion des amitiés (PostgreSQL) - -| Statut | Signification | -|-------------|----------------------------| -| `pending` | Demande envoyée | -| `accepted` | Amis | -| `blocked` | Utilisateur bloqué | -| `rejected` | Demande refusée | - ---- - -## Ressources - -- [Documentation PostgreSQL](https://www.postgresql.org/docs/) -- [Autoriser les OAuth Apps — GitHub](https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/authorizing-oauth-apps) -- [Créer une OAuth App — GitHub](https://docs.github.com/fr/apps/oauth-apps/building-oauth-apps/creating-an-oauth-app) - ---- - -## Journal des modifications - -### BACKEND - -| Date | Description | -|-------|-------------| -| 17/01 | Ajout du service/route pour le système de `game_room` — création/rejoindre une room, destruction automatique si vide, liste des rooms joignables et des joueurs avec scores et état | -| 21/01 | Ajout du service/route pour le système d'avatar — changement, suppression, et récupération de l'avatar | - -### DATABASE - -| Date | Description | -|-------|-------------| -| 17/01 | Ajout des tables `game_rooms`, `game_players`, `game_rounds`, `words` — nom/statut/paramètres de game, joueurs/scores/rôles, historique des rounds, liste des mots | -| 21/01 | Ajout de `avatar_url` dans la table `users` | - ---- - ---- - -# TETRIS - -``` - ████████╗███████╗████████╗██████╗ ██╗███████╗ - ██╔══╝██╔════╝╚══██╔══╝██╔══██╗██║██╔════╝ - ██║ █████╗ ██║ ██████╔╝██║███████╗ - ██║ ██╔══╝ ██║ ██╔══██╗██║╚════██║ - ██║ ███████╗ ██║ ██║ ██║██║███████║ - ╚═╝ ╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝╚══════╝ -``` - -Implémentation du jeu Tetris avec un thème cyberpunk, entièrement découpée en modules JS séparés. - ---- - -## Architecture des fichiers - -``` -srcs/frontend/src/ -├── pieces.js ← Définition des 7 pièces Tetris et leurs rotations -├── tetris.js ← Logique du jeu (classe Tetris) -├── renderer.js ← Rendu canvas (grille principale, hold, next) -└── ui.js ← Glue UI : boutons, overlay, liaison game ↔ DOM -``` - -``` -srcs/frontend/ -└── tetris.html ← Structure HTML de la page - tetris.css ← Styles (thème cyberpunk) -``` - ---- - -## Contrôles clavier - -| Touche | Action | -|-------------|-------------------------------------| -| `←` `→` | Déplacer la pièce horizontalement | -| `↓` | Descente douce (+1 pt) | -| `Espace` | Hard drop — chute instantanée (+2 pts par case) | -| `Q` | Rotation gauche | -| `W` | Rotation droite | -| `C` | Hold — stocker / échanger la pièce courante | - ---- - -## Flux de jeu - -``` - spawn() - │ - ▼ - tick() ──── toutes les timeToDown ms ────┐ - │ │ - ├─ canMoveDown ? ──── oui ──► moveDown() - │ - └─ non ──► lockPiece() - │ - ├─► verifierLignes() (efface + score) - ├─► _makeHarder() (accélération) - └─► spawn() - │ - └─ canSpawn ? ──── non ──► GAME OVER -``` - ---- - -## `pieces.js` — Les pièces Tetris - -### Classe de base : `Piece` - -Classe abstraite dont héritent toutes les pièces du jeu. - -``` -┌─────────────────────────────────────────────────────────────┐ -│ class Piece │ -├─────────────────────────────────────────────────────────────┤ -│ position { x, y } — coordonnées dans la grille │ -│ currentRotation index de la rotation active │ -│ rotations tableau de toutes les formes rotées │ -│ shape forme actuellement active │ -└─────────────────────────────────────────────────────────────┘ -``` - -| Méthode | Description | -|--------------------|-------------| -| `defineRotations()`| Retourne le tableau de toutes les matrices de rotation de la pièce. Surchargée dans chaque sous-classe. | -| `getColor()` | Retourne l'index couleur de la pièce (1 à 7). Surchargée dans chaque sous-classe. | -| `getPosition()` | Retourne une copie de `{ x, y }` — position actuelle dans la grille. | -| `getShape()` | Retourne la matrice 2D de la forme active (rotation courante). | -| `moveDown()` | Incrémente `y` de 1 — descend la pièce d'une case. | -| `moveLeft()` | Décrémente `x` de 1 — déplace la pièce d'une case à gauche. | -| `moveRight()` | Incrémente `x` de 1 — déplace la pièce d'une case à droite. | -| `rotateLeft()` | Passe à la rotation précédente dans le tableau (sens anti-horaire). | -| `rotateRight()` | Passe à la rotation suivante dans le tableau (sens horaire). | - ---- - -### Les 7 pièces — sous-classes de `Piece` - -Chaque pièce définit ses rotations via une matrice `1`/`0` et son index couleur. - -| Classe | Forme | Couleur (index) | Rotations | -|-----------------|-------|-----------------|-----------| -| `PieceT` | T | Violet `1` | 4 | -| `PieceL` | L | Orange `2` | 4 | -| `PieceReverseL` | J | Bleu `3` | 4 | -| `PieceI` | I | Cyan `4` | 2 | -| `PieceZ` | Z | Rouge `5` | 2 | -| `PieceReverseZ` | S | Vert `6` | 2 | -| `PieceO` | O | Jaune `7` | 1 | - -**Exemple — `PieceT` (4 rotations) :** -``` -Rotation 0 Rotation 1 Rotation 2 Rotation 3 - 0 1 0 0 1 0 0 0 0 0 1 0 - 1 1 1 0 1 1 1 1 1 1 1 0 - 0 0 0 0 1 0 0 1 0 0 1 0 -``` - ---- - -## `tetris.js` — Logique du jeu - -### Classe `Tetris` - -``` -┌─────────────────────────────────────────────────────────────────────┐ -│ new Tetris(onRender, onGameOver) │ -├─────────────────────────────────────────────────────────────────────┤ -│ onRender : () => void — callback de rendu à chaque frame │ -│ onGameOver : (score) => void — callback appelé en fin de partie │ -└─────────────────────────────────────────────────────────────────────┘ -``` - -#### État interne - -| Propriété | Type | Description | -|----------------------|-----------|-------------| -| `grid` | `number[][]` | Grille principale 10×20 — `0` = vide, sinon index couleur | -| `bufferGrid` | `number[][]` | Grille 10×5 utilisée pour afficher la pièce suivante | -| `currentPiece` | `Piece` | Pièce en cours de chute | -| `nextPiece` | `Piece` | Prochaine pièce à spawner | -| `storedPiece` | `Piece` | Pièce en hold (stockée par le joueur) | -| `score` | `number` | Score courant | -| `timeToDown` | `number` | Intervalle (ms) entre deux descentes automatiques | -| `hardening` | `number` | Seuil de points avant chaque accélération | -| `decrementTTD` | `number` | Réduction de `timeToDown` à chaque palier | -| `count` | `number` | Accumulateur de points depuis le dernier palier | -| `isRunning` | `boolean` | Vrai si une partie est en cours | -| `isPaused` | `boolean` | Vrai si la partie est en pause | -| `canStore` | `boolean` | Faux si le hold a déjà été utilisé depuis le dernier spawn | - ---- - -### Méthodes publiques - -| Méthode | Description | -|------------------------|-------------| -| `configure(options)` | Applique les paramètres de difficulté — `timeToDown`, `hardening`, `decrementTTD`. Doit être appelé **avant** `start()` pour que `timeToDown` soit pris en compte. | -| `start()` | Initialise et démarre une nouvelle partie (réinitialise la grille, le score, et spawn la première pièce). | -| `stop()` | Arrête la partie — annule la boucle `requestAnimationFrame` et retire l'écouteur clavier. | -| `pause()` | Bascule entre pause et reprise. En reprise, réinitialise `lastTime` pour éviter un saut d'accumulation. | - ---- - -### Méthodes privées - -#### Boucle de jeu - -| Méthode | Description | -|----------------------|-------------| -| `_startGameLoop()` | Lance la boucle via `requestAnimationFrame`. Calcule le `deltaTime` entre chaque frame et accumule le temps. Déclenche `_tick()` dès que l'accumulateur dépasse `timeToDown`. | -| `_tick()` | Un pas logique du jeu : descend la pièce si possible, sinon la verrouille, vérifie les lignes, accélère le jeu, puis spawne la suivante. Si le spawn est impossible → game over. | - -#### Gestion des pièces - -| Méthode | Description | -|------------------------|-------------| -| `_spawnNewPiece()` | Fait de `nextPiece` la pièce courante, génère une nouvelle `nextPiece`, et met à jour la `bufferGrid`. | -| `_createRandomPiece()` | Instancie aléatoirement l'une des 7 pièces à la position de départ `(3, 0)`. | -| `_updateBufferGrid()` | Recrée la grille miniature `bufferGrid` (10×5) centrée sur la forme de `nextPiece`. | -| `_lockPiece()` | Grave la forme et la couleur de `currentPiece` dans `grid` à sa position actuelle. | -| `_storePiece()` | Échange `currentPiece` et `storedPiece` (ou stocke la pièce courante si hold vide). Désactive le hold jusqu'au prochain spawn. | -| `_rotatePiece(dir)` | Tente une rotation (`-1` = gauche, `1` = droite). En cas de collision, essaie des décalages latéraux ou verticaux (wall kick) avant d'annuler. | -| `_hardDrop()` | Téléporte la pièce au bas de sa trajectoire, ajoute +2 pts par case parcourue, puis la verrouille immédiatement. | - -#### Collisions - -| Méthode | Description | -|----------------------|-------------| -| `_canMoveDown()` | Vérifie que chaque cellule de la pièce peut descendre d'une ligne (pas de mur bas, pas de case occupée). | -| `_canMoveLeft()` | Vérifie que chaque cellule peut se déplacer d'une colonne à gauche. | -| `_canMoveRight()` | Vérifie que chaque cellule peut se déplacer d'une colonne à droite. | -| `_isValidPosition()` | Vérifie que la pièce est entièrement dans les limites de la grille et n'occupe aucune case déjà remplie. | -| `_canSpawn()` | Alias de `_isValidPosition()` — utilisé pour détecter le game over après un spawn. | - -#### Score & difficulté - -| Méthode | Description | -|------------------|-------------| -| `verifierLignes()` | Parcourt la grille de bas en haut. Supprime chaque ligne complète, insère une ligne vide en haut. Ajoute les points selon le nombre de lignes effacées simultanément : `0→0 / 1→100 / 2→300 / 3→500 / 4→800`. | -| `_makeHarder()` | Si `count` (points accumulés depuis le dernier palier) dépasse `hardening`, remet `count` à 0 et réduit `timeToDown` de `decrementTTD` (minimum 100 ms). | -| `_gameOver()` | Appelle `stop()` puis déclenche le callback `onGameOver(score)`. | - -#### Entrées clavier - -| Méthode | Description | -|-------------------|-------------| -| `_handleKey(e)` | Écouteur `keydown`. Dispatche l'action selon la touche pressée. Appelle `onRender()` après chaque action. Ignoré si la partie est en pause (sauf pour les touches déjà bloquées par `isRunning`). | - ---- - -### Paramètres de difficulté - -| Paramètre | Défaut | Description | -|---------------|----------|-------------| -| `timeToDown` | `1000 ms`| Intervalle entre deux descentes automatiques. Plus faible = plus rapide. | -| `hardening` | `1000 pts`| Points cumulés avant chaque accélération. Plus élevé = progression plus lente. | -| `decrementTTD`| `100 ms` | Réduction de `timeToDown` à chaque palier. Plus élevé = accélération plus brutale. | - ---- - -## `renderer.js` — Rendu canvas - -### Constantes - -| Constante | Valeur | Description | -|-----------|--------|-------------| -| `CELL` | `30` | Taille en pixels d'une cellule dans la grille principale | -| `COLORS` | tableau| 8 couleurs indexées de `0` (fond) à `7` (jaune) — thème cyberpunk | - -### Fonctions - -| Fonction | Description | -|---------------------------------------|-------------| -| `drawCell(ctx, x, y, colorIndex, size)` | Dessine une cellule colorée à la position `(x, y)` avec un effet 3D : highlight blanc en haut/gauche, ombre noire en bas/droite. | -| `clearCanvas(ctx, w, h)` | Efface un canvas en le remplissant avec la couleur de fond `#070712`. | -| `drawGridLines(ctx, cols, rows, size)`| Trace la grille de fond en lignes semi-transparentes (opacité 4 %). | -| `drawGhost(ctx, piece, grid)` | Calcule et affiche la "pièce fantôme" — projection de la pièce courante au bas de sa trajectoire, dessinée en contour blanc semi-transparent. | -| `drawMiniPiece(ctx, piece, w, h)` | Dessine une pièce centrée dans un petit canvas (pour les panneaux **Next** et **Hold**), en utilisant des cellules de 20 px. | -| `render()` | Fonction de rendu principale appelée à chaque frame. Redessine : la grille principale, les cellules verrouillées, la pièce ghost, la pièce courante, les panneaux Next/Hold, et le score. | - ---- - -## `ui.js` — Interface utilisateur - -### Fonctions - -| Fonction | Description | -|-----------------------------|-------------| -| `updateButtons()` | Met à jour l'état des boutons et des inputs selon `game.isRunning` et `game.isPaused` — désactive Start si en cours, active Pause/Stop seulement pendant une partie, bascule le label entre "Pause" et "Resume", verrouille les inputs de settings pendant le jeu. | -| `showOverlay(title, score)` | Affiche l'overlay (écran superposé) avec un titre et optionnellement un score. Utilisé pour "GAME OVER", "PAUSE", et "STOPPED". | -| `hideOverlay()` | Retire la classe `visible` de l'overlay pour le masquer. | -| `applySettings()` | Lit les valeurs des trois inputs de configuration et appelle `game.configure(...)` pour mettre à jour les paramètres de difficulté. | - -### Initialisation - -```js -const game = new Tetris( - () => { render(); updateButtons(); }, // onRender - (score) => { render(); updateButtons(); showOverlay('GAME OVER', score); } // onGameOver -); -``` - -| Événement | Action | -|----------------------------------|--------| -| Clic `btn-start` | Masque l'overlay, démarre la partie, met à jour UI et canvas | -| Clic `btn-pause` | Bascule pause, affiche ou masque l'overlay "PAUSE" | -| Clic `btn-stop` | Arrête la partie, affiche l'overlay "STOPPED" | -| `change` sur les inputs settings | Appelle `applySettings()` pour reconfigurer la difficulté | - ---- - -## Système de couleurs - -``` -Index Couleur Hex Pièce - 0 Fond #070712 (vide) - 1 Violet #a855f7 PieceT - 2 Orange #f97316 PieceL - 3 Bleu #3b82f6 PieceReverseL - 4 Cyan #06b6d4 PieceI - 5 Rouge #ef4444 PieceZ - 6 Vert #22c55e PieceReverseZ - 7 Jaune #eab308 PieceO -``` diff --git a/Transcendence/a_faire.txt b/Transcendence/a_faire.txt new file mode 100644 index 0000000..5a49ba9 --- /dev/null +++ b/Transcendence/a_faire.txt @@ -0,0 +1,64 @@ +le you win apparait sur la grille de l'adversaire, elle doit apparaitre sur la grille principale du joueur qui gagne FIXED ? [OUI] + +undefined is not an object (evaluating 'grid[ny][nx]') +a la ligne 56 de renderer.js FIXED ? [CA_EN_A_L'AIR] +A tester plusieurs game, si ca freeze et que l'erreur reviens, NAN, j'ai changer les limite +sur les bord gauche droit de la grid, a voir + +Quand je fais pause, +ca ne fait pas pose pour tout le monde FIXED[OUI] + +Quand je fait stop, +ca ne fais pas stop pour tout le monde FIXED[OUI] + +durant le duel du tetris en ligne: +j'ai plusieurs probleme: + + +Les parametre doivent etre les memes pour tout le monde +FIXED[OUI] + + +DES GAMES OVER ARRIVE COMME CA SANS RAISON durant le duel FIXED[OUI] + + est-ce du au valid-block ? au addGarbage ? + +Bug A — Faux game over via garbage (tetris.js) + +addGarbageLines appelait _isValidPosition() qui retourne false si gy < 0. +Or après garbage, la pièce monte légitimement au-dessus de la grille +(y négatif). +Fix : nouvelle méthode _isValidPositionAllowTop() qui +ignore les cellules au-dessus de la grille (zone tampon) et +ne vérifie que les collisions réelles dans la grille. + +Bug B — Crash si la pièce est au-dessus de la grille (tetris.js) + +_canMoveDown, _canMoveLeft, _canMoveRight, et _lockPiece accèdent à grid[y + row] +sans vérifier si y + row < 0 → undefined → crash. Fix : +skip des rangées hors grille avec continue. + +Bug C — Game over par garbage ne termine pas le duel (duel.js) + +onLocalGameOver ne faisait endDuel() que si validBlock=true. Un game over réel dû à du garbage (validBlock=false) laissait le duel dans un état cassé et l'adversaire ne voyait jamais "YOU WIN". Fix : endDuel() systématique, et OPPONENT_GAME_OVER affiche toujours "YOU WIN". + + + +separer le code tetris du reste, de meme pour les sockets. FAIT[?] + + +error: +renderer.js:56 Uncaught TypeError: Cannot read properties of undefined (reading 'length') + at drawGhost (renderer.js:56:71) + at render (renderer.js:101:9) + at Tetris.onRender (ui.js:107:9) + at gameLoop (tetris.js:115:18) + +FIXED[ON DIRAIS BIEN] + +Il faut verifier si le garbage si retrouve a la premiere ligne, +si c'est le cas, game-over + +Il faut un bouton restart + +system d'attribution de point et d'enregistrement de point \ No newline at end of file diff --git a/Transcendence/srcs/backend/services/socket.js b/Transcendence/srcs/backend/services/socket.js index cba814b..019e424 100644 --- a/Transcendence/srcs/backend/services/socket.js +++ b/Transcendence/srcs/backend/services/socket.js @@ -493,6 +493,39 @@ function setupSocketIO(io) } }); + // pause → relayé aux DEUX joueurs de la room + socket.on('tetris:pause', () => { + const code = socket.tetrisRoomCode; + if (!code) return; + const room = tetrisRooms.get(code); + if (!room) return; + for (const s of room.values()) { + s.emit('tetris:pause'); + } + }); + + // stop → relayé aux DEUX joueurs de la room + socket.on('tetris:stop', () => { + const code = socket.tetrisRoomCode; + if (!code) return; + const room = tetrisRooms.get(code); + if (!room) return; + for (const s of room.values()) { + s.emit('tetris:stop'); + } + }); + + // settings → relayé aux DEUX joueurs de la room + socket.on('tetris:settings', (data) => { + const code = socket.tetrisRoomCode; + if (!code) return; + const room = tetrisRooms.get(code); + if (!room) return; + for (const s of room.values()) { + s.emit('tetris:settings', data); + } + }); + // game-over → relayé en opponent-game-over chez l'adversaire socket.on('tetris:game-over', (data) => { _tetrisRelayToOpponent(socket, 'tetris:opponent-game-over', data); diff --git a/Transcendence/srcs/frontend/src/ajout du multiplayer b/Transcendence/srcs/frontend/src/ajout du multiplayer deleted file mode 100644 index 512ff84..0000000 --- a/Transcendence/srcs/frontend/src/ajout du multiplayer +++ /dev/null @@ -1,45 +0,0 @@ -Je veux faire un mode duel du tetris, -il est fonctionnel est solo. - -Pour commencer, je vais devoir -creer une div qui sera le rendu -du joueur second joueur. il sera a droite de la -div principale qui lui meme sera legerement decale vers la gauche, -cette div sera grossierement identitique en style qui la div principale - -je ne vais pas voir en temps reelle -les pieces du joueur qui tombe. -A la place quand le joueur 2 a mis une piece, il envoie un signal, -le joueur 1 envoie un signal egalement quand la piece tombe. - -en mode duel, quand une ligne est clear, il envoie la ligne -moins la cellule qui a provoquer le clear (donc avec un trou pile -la ou la derniere piece est arrive) au joueur opposant. - -Ce qui a pour effet de decaler toute les lignes vers le haut -a l'opposant pour recevoir la ligne recu avec le trou. - -Pour se faire je vais devoir connecter les deux joueurs. - -Il me faudra : - -une class Duel il aura pour methode et membre: - -action_queue: c'est un tableau qui repertorie tous les -signaux a traiter, c'est un tableau qui sera partager -entre le joueur1 et le joueur2 - - -Syncronise_game: fonction qui traite les -actions de action_queue et qui verifie l'integrite du duel, -il va par exemple regarder l'etat du jeu de chaque joueur -pour voir s'il correspond bien a ce qui est attendu - -il y aura different type de signaux. -Le signal bloc pose avec le type de bloc, -sa rotation et as position - -le signal line cleared, avec le nombre de ligne -cleared et on ajoute le trou aussi - -Aucune idee de si je dois utiliser web socket ou autre diff --git a/Transcendence/srcs/frontend/src/duel.js b/Transcendence/srcs/frontend/src/duel.js index 859d99e..70acee1 100644 --- a/Transcendence/srcs/frontend/src/duel.js +++ b/Transcendence/srcs/frontend/src/duel.js @@ -54,9 +54,16 @@ class Duel { this.socket.emit('tetris:lines-cleared', { count, holeCol, garbageLines }); } - onLocalGameOver(score) { + onLocalGameOver(score, validBlock) { if (!this.isReady) return; - this.socket.emit('tetris:game-over', { score }); + this.socket.emit('tetris:game-over', { score, validBlock }); + this.endDuel(); + } + + endDuel() { + this.isReady = false; + this.action_queue = []; + if (this.tetrisGame.isRunning) this.tetrisGame.stop(); } // ─── Traitement de la queue ─────────────── @@ -82,7 +89,8 @@ class Duel { break; case 'OPPONENT_GAME_OVER': - this._showOpponentOverlay('YOU WIN', action.score); + showOverlay('YOU WIN', action.score); + this.endDuel(); break; } } @@ -116,12 +124,48 @@ class Duel { }); this.socket.on('tetris:opponent-game-over', (data) => { - this.action_queue.push({ type: 'OPPONENT_GAME_OVER', score: data.score }); + this.action_queue.push({ type: 'OPPONENT_GAME_OVER', score: data.score, validBlock: data.validBlock }); }); this.socket.on('tetris:start-duel', () => { if (this.onStart) this.onStart(); }); + + this.socket.on('tetris:pause', () => { + this.tetrisGame.pause(); + updateButtons(); + if (this.tetrisGame.isPaused) showOverlay('PAUSE'); + else hideOverlay(); + }); + + this.socket.on('tetris:stop', () => { + this.tetrisGame.stop(); + updateButtons(); + render(); + showOverlay('STOPPED'); + }); + + this.socket.on('tetris:settings', (data) => { + document.getElementById('input-ttd').value = data.timeToDown; + document.getElementById('input-hardening').value = data.hardening; + document.getElementById('input-decrement').value = data.decrementTTD; + this.tetrisGame.configure(data); + }); + } + + togglePause() { + if (!this.isReady) return; + this.socket.emit('tetris:pause'); + } + + stop() { + if (!this.isReady) return; + this.socket.emit('tetris:stop'); + } + + syncSettings(settings) { + if (!this.isReady) return; + this.socket.emit('tetris:settings', settings); } // ─── Utilitaires ───────────────────────── diff --git a/Transcendence/srcs/frontend/src/renderer.js b/Transcendence/srcs/frontend/src/renderer.js index 4473641..f033660 100644 --- a/Transcendence/srcs/frontend/src/renderer.js +++ b/Transcendence/srcs/frontend/src/renderer.js @@ -53,7 +53,7 @@ function drawGhost(ctx, piece, grid) { if (shape[row][col] !== 0) { const ny = ghost.y + row; const nx = ghost.x + col; - if (ny >= grid.length || grid[ny][nx] !== 0) valid = false; + if (ny < 0 || ny >= grid.length || nx < 0 || nx >= grid[ny].length || grid[ny][nx] !== 0) valid = false; } if (!valid) { ghost.y--; break; } } diff --git a/Transcendence/srcs/frontend/src/tetris.css b/Transcendence/srcs/frontend/src/tetris.css index 5c028a7..9c37b1e 100644 --- a/Transcendence/srcs/frontend/src/tetris.css +++ b/Transcendence/srcs/frontend/src/tetris.css @@ -55,11 +55,11 @@ h1 { z-index: 1; } -/* ── Section locale (légèrement décalée à gauche par le flex naturel) ── */ +/* ── Section locale ── */ #local-section { display: flex; flex-direction: column; - align-items: center; + align-items: flex-start; } #app { @@ -165,11 +165,37 @@ button { width: 100%; } -#btn-start { color: var(--accent); border-color: var(--accent); } -#btn-start:hover:not(:disabled) { background: var(--accent); color: var(--bg); box-shadow: 0 0 15px var(--accent); } +#btn-start { + color: var(--accent); + border-color: var(--accent); +} -#btn-pause { color: var(--accent2); border-color: var(--accent2); } -#btn-pause:hover:not(:disabled) { background: var(--accent2); color: var(--bg); box-shadow: 0 0 15px var(--accent2); } +#btn-start:hover:not(:disabled) +{ + background: var(--accent); + color: var(--bg); + box-shadow: 0 0 15px var(--accent); +} + +#btn-restart { + color: var(--accent); + border-color: var(--accent); +} + +#btn-restart:hover:not(:disabled){ + background: var(--accent); + color: var(--bg); + box-shadow: 0 0 15px var(--accent); +} + +#btn-pause { + color: var(--accent2); + border-color: var(--accent2); +} +#btn-pause:hover:not(:disabled) { + background: var(--accent2); + color: var(--bg); box-shadow: 0 0 15px var(--accent2); +} #btn-stop { color: #ef4444; border-color: #ef4444; } #btn-stop:hover:not(:disabled) { background: #ef4444; color: var(--bg); box-shadow: 0 0 15px #ef4444; } @@ -293,21 +319,26 @@ button:disabled { opacity: 0.3; cursor: not-allowed; } #duel-status.waiting { color: #f97316; } #duel-status.ready { color: var(--accent); } +/* ── Colonne gauche (panel + settings empilés) ── */ +#left-column { + display: flex; + flex-direction: column; + gap: 16px; + width: 130px; + flex-shrink: 0; +} + /* ── Settings Panel ── */ #settings-panel { background: var(--panel); border: 1px solid var(--border); border-radius: 6px; - padding: 14px 20px; - margin-top: -250px; - margin-left: -600px; + padding: 14px; box-shadow: 0 0 20px rgba(0,255,231,0.05); - position: relative; - z-index: 1; display: flex; flex-direction: column; gap: 10px; - width: fit-content; + width: 130px; } .settings-title { @@ -322,10 +353,9 @@ button:disabled { opacity: 0.3; cursor: not-allowed; } .settings-row { display: flex; - align-items: center; - justify-content: space-between; - gap: 16px; - font-size: 0.6rem; + flex-direction: column; + gap: 4px; + font-size: 0.55rem; color: var(--dim); letter-spacing: 0.05em; } @@ -338,7 +368,7 @@ button:disabled { opacity: 0.3; cursor: not-allowed; } font-family: 'Orbitron', monospace; font-size: 0.65rem; padding: 4px 8px; - width: 80px; + width: 100%; text-align: right; outline: none; transition: border-color 0.2s; diff --git a/Transcendence/srcs/frontend/src/tetris.html b/Transcendence/srcs/frontend/src/tetris.html index 23bdb68..1263ea6 100644 --- a/Transcendence/srcs/frontend/src/tetris.html +++ b/Transcendence/srcs/frontend/src/tetris.html @@ -28,20 +28,40 @@