Compare commits

...

10 Commits

Author SHA1 Message Date
florian
9d3f4714d2 remake hologram 2025-05-07 08:26:27 +02:00
florian
4a8299e5a6 add drawHologram function to display the current piece's shadow on the board 2025-05-04 11:19:37 +02:00
florian
f70c427095 remove datas directory 2025-05-04 11:09:25 +02:00
florian
154ef7f486 add game logic, display functions, and menu controls for Tetris 2025-05-04 11:06:51 +02:00
flo
2d34f975bc add high score feature and update dependencies 2025-04-29 14:38:06 +02:00
flo
d8dcb08a46 continu change style 2025-04-25 09:36:40 +02:00
flo
60f424ce1e change color 2025-04-24 08:53:52 +02:00
flo
f26e4c553a change piece and color 2025-04-23 14:09:50 +02:00
flo
2052f8ed3b hologramme and pause menu 2025-04-23 11:06:37 +02:00
flo
02b55431a3 add pause and preview 2025-04-11 11:34:10 +02:00
20 changed files with 3307 additions and 213 deletions

2
.gitignore vendored
View File

@ -148,3 +148,5 @@ fastdeliver.db
api.http api.http
node_modules node_modules
datas

6
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"cSpell.words": [
"elle",
"fixée"
]
}

187
index.js
View File

@ -1,4 +1,36 @@
import express from 'express'; import express from 'express';
import sqlite3 from 'sqlite3';
import { open } from 'sqlite';
import fs from 'fs';
// Déclaration de la variable db pour qu'elle soit accessible dans tout le script
let db;
// Fonction pour initialiser la base de données
async function initializeDatabase() {
// open the database - SQLite créera automatiquement le fichier s'il n'existe pas
db = await open({
filename: './datas/database.db',
driver: sqlite3.Database
});
// Créer les tables nécessaires si elles n'existent pas
await db.exec(`
CREATE TABLE IF NOT EXISTS scores (
id INTEGER PRIMARY KEY AUTOINCREMENT,
player_name TEXT,
score INTEGER,
pathHistory TEXT,
date TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
`);
console.log('Database initialized');
}
initializeDatabase().catch(err => {
console.error('Error initializing database:', err);
});
const app = express(); const app = express();
const port = 3000; const port = 3000;
@ -6,6 +38,161 @@ const port = 3000;
app.use(express.static('public')); app.use(express.static('public'));
app.use(express.json()); app.use(express.json());
app.get('/', (req, res) => {
res.sendFile('index.html', { root: '.' });
});
const templatePiece = [
[ // ok
[3, 0],
[4, 0],
[5, 0],
[6, 0],
[4.5, 0.5]
],
[
[3, 1],
[4, 1],
[5, 1],
[5, 0],
[4, 1]
],
[ // ok
[3, 1],
[4, 1],
[5, 1],
[3, 0],
[4, 1]
],
[ // ok
[3, 0],
[4, 0],
[4, 1],
[5, 1],
[4, 1]
],
[
[3, 1],
[4, 1],
[4, 0],
[5, 0],
[4, 1]
],
[ // ok
[3, 0],
[4, 0],
[4, 1],
[5, 0],
[4, 0]
],
[ // ok
[4, 0],
[5, 0],
[4, 1],
[5, 1],
[4.5, 0.5]
]
];
const templateColor = [
[240, 87, 84, 255],
[254, 223, 92, 255],
[27, 148, 118, 255],
[36, 115, 155, 255],
[106, 190, 178, 255],
[164, 199, 218, 255],
[177, 225, 218, 255]
];
app.put('/api/sendScore', (req, res) => {
let score = req.body.score;
// let history = req.body.history;
let lines = req.body.lines;
let playerName = req.body.playerName;
// Vérifier si le score est un nombre valide
if (isNaN(score) || score < 0) {
return res.status(400).send('Invalid score');
}
// Vérifier si l'historique est un tableau valide
// if (!Array.isArray(history)) {
// return res.status(400).send('Invalid history');
// }
// Vérifier si le nom du joueur est une chaîne de caractères valide
if (typeof playerName !== 'string' || playerName.trim() === '') {
return res.status(400).send('Invalid player name');
}
// Vérifier si le score est supérieur à 0 et inférieur à 1200 * (lines/4)
if ((lines/4) * 1200 < score) {
return res.status(400).send('Invalid score');
}
// Vérifier si la line est un nombre valide
if (isNaN(lines) || lines < 0) {
return res.status(400).send('Invalid lines');
}
// Vérifier si le dossier des historiques existe, le créer si nécessaire
// const historyDir = './datas/histories';
// if (!fs.existsSync(historyDir)) {
// fs.mkdirSync(historyDir, { recursive: true });
// }
// Date du jour
// const date = new Date();
// const dateString = date.toISOString().split('T')[0]; // Format YYYY-MM-DD
// // Vérifier si un fichier avec ce nom existe déjà et ajouter un chiffre si nécessaire
// let finalPlayerName = playerName + dateString;
// let counter = 0;
// let historyFilePath = `${historyDir}/${finalPlayerName}.json`;
// while (fs.existsSync(historyFilePath)) {
// counter++;
// finalPlayerName = `${playerName}${dateString}${counter}`;
// historyFilePath = `${historyDir}/${finalPlayerName}.json`;
// }
// // Écrire l'historique dans le fichier
// try {
// fs.writeFileSync(historyFilePath, JSON.stringify(history));
// console.log(`History written to ${historyFilePath}`);
// } catch (err) {
// console.error('Error writing history file:', err);
// return res.status(500).send('Error writing history file');
// }
//
db.run('INSERT INTO scores (player_name, score, pathHistory) VALUES (?, ?, ?)', [playerName, score, "/"])
.then(() => {
console.log(`Score inserted for ${playerName}`);
})
.then(() => {
console.log('History path inserted in database');
res.status(200).json({
success: true,
playerName: playerName,
message: 'Score and history saved'
});
})
.catch(err => {
console.error('Database error:', err);
res.status(500).send('Error inserting data');
});
});
app.get('/api/getHighScores', async (req, res) => {
try {
const scores = await db.all('SELECT player_name, score FROM scores ORDER BY score DESC LIMIT 10');
res.json(scores);
} catch (err) {
console.error('Database error:', err);
res.status(500).send('Error retrieving high scores');
}
}
);
app.listen(port, () => { app.listen(port, () => {
console.log(`Server listening at http://localhost:${port}`); console.log(`Server listening at http://localhost:${port}`);
}); });

1457
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -12,7 +12,9 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"express": "^4.21.2" "express": "^4.21.2",
"sqlite": "^5.1.1",
"sqlite3": "^5.1.7"
}, },
"devDependencies": { "devDependencies": {
"nodemon": "^3.1.9" "nodemon": "^3.1.9"

Binary file not shown.

BIN
public/img/pause.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 376 B

View File

@ -4,41 +4,82 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<title>Tetris on the web</title> <title>Tetris on the web</title>
<script src="./script/index.js" defer></script> <script src="./script/element.js" defer></script>
<script src="./script/var.js" defer></script>
<script src="./script/displayGameFunction.js" defer></script>
<script src="./script/displayMenuFunction.js" defer></script>
<script src="./script/contruct.js" defer></script>
<script src="./script/magicVariable.js" defer></script>
<script src="./script/gameFunction.js" defer></script>
<script src="./script/controller.js" defer></script>
<script src="./script/menuController.js" defer></script>
<link rel="stylesheet" href="./style/index.css" /> <link rel="stylesheet" href="./style/index.css" />
</head> </head>
<body> <body>
<template id="high-score-template">
<div class="high-score">
<span class="high-score-name"></span>
<span class="high-score-score"></span>
</div>
</template>
<div class="frame scoreboard">
<p>High Scores</p>
<div id="high-scores-container">
<div id="high-scores-list" class="high-scores-list">
</div>
</div>
</div>
<div class="container"> <div class="container">
<div id="left-part" class="first"> <div id="left-part" class="first">
<p> <p class="frame">
Score: Score:
<span id="score-value">0</span> <span id="score-value">0</span>
</p> </p>
</div> </div>
<div id="tetris" class="tetris"> <div id="tetris" class="frame tetris">
<canvas class="hidden" id="tetris-canvas" width="300" height="600"></canvas> <div class="hidden" id="board-container">
<div class="main-menu hidden" id="main-menu" > <canvas id="tetris-canvas" width="300" height="600"></canvas>
<button id="start-button">Start</button>
</div> </div>
<div class="gameOver hidden"> <div class="main-menu menu" id="main-menu" >
<label id="start-button" class="button">Start</label>
</div>
<div class="gameOver-menu menu hidden" id="game-over-menu">
<p>Game Over</p> <p>Game Over</p>
<button id="restart-button">Restart</button> <label id="restart-button" class="button">Restart</label>
<button id="main-menu">Main Menu</button> <label id="main-menu-button2">Main Menu</label>
</div> </div>
<div class="pause hidden" id="pause-menu"> <div class="pause-menu menu hidden" id="pause-menu">
<p>Paused</p> <p>Paused</p>
<button id="resume-button">Resume</button> <label id="resume-button" class="button">Resume</label>
<button id="main-menu">Main Menu</button> <label id="main-menu-button" class="button">Main Menu</label>
</div> </div>
<div class="load-menu" id="load-menu"> <div class="load-menu menu hidden" id="load-menu">
<span class="loader"></span> <span class="loader"></span>
<p>Loading...</p> <p>Loading...</p>
</div> </div>
</div> </div>
<div class="last"></div> <div class="last">
<div class="frame">
<p>Next</p>
<div id="preview-container">
<canvas id="preview-canvas" width="100" height="100"></canvas>
</div>
</div>
<div class="frame pauseContainer" id="pause-container">
<div id="pause-button" class="icon play-pause">
</div>
</div>
</div>
</div> </div>
<div class="options">
</div>
</body> </body>
</html> </html>

149
public/script/controller.js Normal file
View File

@ -0,0 +1,149 @@
function initGame() {
displayGameBoard();
game = getGame();
setSpeed(game);
initOrChangeDropInterval(game.speed);
// On rafraichit le plateau toutes les 16ms (environ 60 FPS)
refreshInterval = setInterval(() => {
refreshBoard(game.board);
displayPreview(game);
scoreElement.innerText = game.score;
}, 16);
history = [];
}
function initOrChangeDropInterval(speed) {
if (dropInterval) {
clearInterval(dropInterval);
}
dropInterval = setInterval(() => {
dropPiece();
}, speed);
}
function moveDownWithCheck(game) {
let piece = game.currentPiece;
if (ifMoveDown(piece, game)) {
opacityCurrentPiece = 1;
clearInterval(endDropInterval);
endDropInterval = null;
moveDown(piece);
}
checkEnDrop(game);
}
function checkEnDrop(game) {
if (ifMoveDown(game.currentPiece, game)) {
opacityCurrentPiece = 1;
clearInterval(endDropInterval);
endDropInterval = null;
}
else {
endDrop(game);
}
}
function dropPiece() {
moveDownWithCheck(game);
}
function pauseGame() {
if (game.paused) {
game.paused = false;
initOrChangeDropInterval(game.speed);
refreshInterval = setInterval(() => {
refreshBoard(game.board);
displayPreview(game);
}, 16);
displayGameBoard();
} else {
game.paused = true;
clearInterval(dropInterval);
clearInterval(refreshInterval);
dropInterval = null;
refreshInterval = null;
endDropInterval = null;
displayPause()
}
}
document.addEventListener('keydown', (event) => {
if (event.key == 'Escape' && !keyPress.includes('Escape')) {
pauseGame();
return;
}
if (game.paused) {
return;
}
if (game.gameOver) {
return;
}
if ((event.key == 'ArrowUp' || event.key == 'w' || event.key == 'z') && !keyPress.includes('ArrowUp') && !keyPress.includes('w') && !keyPress.includes('z')) {
rotatePiece(game.currentPiece);
checkEnDrop(game);
} else if ((event.key == 'ArrowDown' || event.key == 's') && !keyPress.includes('ArrowDown') && !keyPress.includes('s')) {
initOrChangeDropInterval(60);
} else if ((event.key == 'ArrowLeft' || event.key == 'a' || event.key == 'q') && !keyPress.includes('ArrowLeft') && !keyPress.includes('q') && !keyPress.includes('a')) {
moveLeft(game.currentPiece);
checkEnDrop(game);
leftInterval = setInterval(() => {
moveLeft(game.currentPiece);
checkEnDrop(game);
}, 100);
} else if (((event.key == 'ArrowRight') || event.key == 'd') && !keyPress.includes('ArrowRight') && !keyPress.includes('d')) {
moveRight(game.currentPiece);
checkEnDrop(game);
rightInterval = setInterval(() => {
moveRight(game.currentPiece);
checkEnDrop(game);
}, 100);
} else if (event.key == ' ' && !keyPress.includes(' ')) {
fastDrop(game);
}
console.log(event.key);
keyPress.push(event.key);
});
document.addEventListener('keyup', (event) => {
if (!game.paused && !game.gameOver) {
if (event.key == 'ArrowDown' || event.key == 's') {
initOrChangeDropInterval(game.speed);
} else if (event.key == 'ArrowLeft' || event.key == 'a' || event.key == 'q') {
clearInterval(leftInterval);
} else if (event.key == 'ArrowRight' || event.key == 'd') {
clearInterval(rightInterval);
}
}
keyPress = keyPress.filter((key) => {
return key != event.key;
});
});

160
public/script/contruct.js Normal file
View File

@ -0,0 +1,160 @@
function getGame() {
return {
board: [],
currentPiece: getRandomPiece(),
nextPiece: getRandomPiece(),
score: 0,
level: 1,
speed: 1000,
lines: 0,
linesCleared: 0,
paused: false,
gameOver: false
}
}
function getColor(r, g, b) {
return {
red: r,
green: g,
blue: b
}
}
function getSquare(xPos, yPos, c) {
return {
color: c,
x: xPos,
y: yPos
}
}
function getBarPiece() {
return {
squares: [
getSquare(3, 0, getColor(240, 87, 84)),
getSquare(4, 0, getColor(240, 87, 84)),
getSquare(5, 0, getColor(240, 87, 84)),
getSquare(6, 0, getColor(240, 87, 84))
],
xCenter: 4.5,
yCenter: 0.5
}
}
function getLPiece() {
return {
squares: [
getSquare(3, 1, getColor(254, 223, 92)),
getSquare(4, 1, getColor(254, 223, 92)),
getSquare(5, 1, getColor(254, 223, 92)),
getSquare(5, 0, getColor(254, 223, 92))
],
xCenter: 4,
yCenter: 1
}
}
function getJPiece() {
return {
squares: [
getSquare(3, 1, getColor(27, 148, 118)),
getSquare(4, 1, getColor(27, 148, 118)),
getSquare(5, 1, getColor(27, 148, 118)),
getSquare(3, 0, getColor(27, 148, 118))
],
xCenter: 4,
yCenter: 1
}
}
function getTPiece() {
return {
squares: [
getSquare(3, 1, getColor(36, 115, 155)),
getSquare(4, 1, getColor(36, 115, 155)),
getSquare(5, 1, getColor(36, 115, 155)),
getSquare(4, 0, getColor(36, 115, 155))
],
xCenter: 4,
yCenter: 1
}
}
function getZPiece() {
return {
squares: [
getSquare(3, 1, getColor(106, 190, 178)),
getSquare(4, 1, getColor(106, 190, 178)),
getSquare(4, 0, getColor(106, 190, 178)),
getSquare(5, 0, getColor(106, 190, 178))
],
xCenter: 4,
yCenter: 1
}
}
function getSPiece() {
return {
squares: [
getSquare(3, 0, getColor(164, 199, 218)),
getSquare(4, 0, getColor(164, 199, 218)),
getSquare(4, 1, getColor(164, 199, 218)),
getSquare(5, 1, getColor(164, 199, 218))
],
xCenter: 4,
yCenter: 1
}
}
function getSquarePiece() {
return {
squares: [
getSquare(3, 0, getColor(177, 225, 218)),
getSquare(4, 0, getColor(177, 225, 218)),
getSquare(3, 1, getColor(177, 225, 218)),
getSquare(4, 1, getColor(177, 225, 218))
],
xCenter: 3.5,
yCenter: 0.5
}
}
function getPiece(type) {
switch (type) {
case 'bar':
return getBarPiece();
case 'L':
return getLPiece();
case 'J':
return getJPiece();
case 'T':
return getTPiece();
case 'Z':
return getZPiece();
case 'S':
return getSPiece();
case 'square':
return getSquarePiece();
}
}
function getRandomPiece() {
const pieces = ['bar', 'L', 'J', 'T', 'Z', 'S', 'square'];
const randomIndex = Math.floor(Math.random() * pieces.length);
return getPiece(pieces[randomIndex]);
}
function clonePiece(piece) {
let newSquares = piece.squares.map(square => ({
color: { ...square.color },
x: square.x,
y: square.y
}));
return {
squares: newSquares,
xCenter: piece.xCenter,
yCenter: piece.yCenter
};
}

View File

@ -0,0 +1,166 @@
let boardContext = board.getContext('2d');
let previewContext = preview.getContext('2d');
function clearBoard() {
// Ajouter un motif subtil en arrière-plan
boardContainer.innerHTML = '';
board = boardBis.cloneNode(true);
boardContainer.appendChild(board);
boardContext = board.getContext('2d');
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 20; j++) {
if ((i + j) % 2 === 0) {
boardContext.fillStyle = '#6d728020'; // Encore plus sombre
}
else {
boardContext.fillStyle = '#3d425020'; // Presque noir-bleuté
}
boardContext.fillRect(
i * board.width/10,
j * board.height/20,
board.width/10,
board.height/20
);
}
}
}
function drawGrid() {
boardContext.strokeStyle = 'gray'; // Couleur des lignes du quadrillage
boardContext.lineWidth = 0.5; // Épaisseur des lignes
// Dessiner les lignes verticales
for (let x = 0; x <= board.width; x += board.width / 10) {
boardContext.beginPath();
boardContext.moveTo(x, 0);
boardContext.lineTo(x, board.height);
boardContext.stroke();
}
// Dessiner les lignes horizontales
for (let y = 0; y <= board.height; y += board.height / 20) {
boardContext.beginPath();
boardContext.moveTo(0, y);
boardContext.lineTo(board.width, y);
boardContext.stroke();
}
}
function drawCurrentPiece() {
let piece = game.currentPiece;
let squares = piece.squares;
for (let i = 0; i < squares.length; i++) {
let square = squares[i];
drawSquare(square.x, square.y, square, opacityCurrentPiece);
}
}
function drawSquare(x, y, square, opacity = 1) {
let color = square.color;
boardContext.fillStyle = '#303f4f'; // Couleur de fond
boardContext.fillRect(
x * board.width/10,
y * board.height/20,
board.width/10,
board.height/20
);
let margin = 3;
boardContext.fillStyle = `rgba(${color.red}, ${color.green}, ${color.blue}, ${opacity})`;
boardContext.fillRect(
x * board.width/10 + margin,
y * board.height/20 + margin,
board.width/10 - margin * 2,
board.height/20 - margin * 2
);
}
function refreshBoard() {
clearBoard();
drawGrid();
for (let i = 0; i < game.board.length; i++) {
let square = game.board[i];
drawSquare(square.x, square.y, square);
}
drawHologram();
drawCurrentPiece();
}
function drawHologram() {
let pieceBis = clonePiece(game.currentPiece);
while (ifMoveDown(pieceBis, game)) {
moveDown(pieceBis);
}
for (let i = 0; i < pieceBis.squares.length; i++) {
let square = pieceBis.squares[i];
drawSquare(square.x, square.y, square, 0.5);
}
}
function displayPreview(game) {
previewContainer.innerHTML = '';
preview = previewBis.cloneNode(true);
previewContainer.appendChild(preview);
previewContext = preview.getContext('2d');
// make damier
for (let x = 0; x < 5 ; x++) {
for (let y = 0;y < 5; y++) {
if ((x + y) % 2 === 0) {
previewContext.fillStyle = '#6d728020'; // Encore plus sombre
}
else {
previewContext.fillStyle = '#3d425020'; // Presque noir-bleuté
}
previewContext.fillRect(
x * preview.width/5,
y * preview.height/5,
preview.width/5,
preview.height/5
);
}
}
let nextPiece = game.nextPiece;
let xDif = nextPiece.xCenter - 2;
let yDif = nextPiece.yCenter - 2;
for (let i = 0; i < nextPiece.squares.length; i++) {
let square = nextPiece.squares[i];
previewContext.fillStyle = '#303f4f'; // Couleur de fond
previewContext.fillRect(
(square.x - xDif) * preview.width/5,
(square.y - yDif) * preview.height/5,
preview.width/5,
preview.height/5
);
let margin = 3;
previewContext.fillStyle = `rgba(${square.color.red}, ${square.color.green}, ${square.color.blue}, 1)`;
previewContext.fillRect(
(square.x - xDif) * preview.width/5 + margin,
(square.y - yDif) * preview.height/5 + margin,
preview.width/5 - margin * 2,
preview.height/5 - margin * 2
);
}
}

View File

@ -0,0 +1,39 @@
function displayMainMenu() {
mainMenu.classList.remove('hidden');
loadMenu.classList.add('hidden');
boardContainer.classList.add('hidden');
pauseButton.classList.remove('paused');
buttonContainer.classList.add('hidden');
gameOverMenu.classList.add('hidden');
}
function displayPause() {
mainMenu.classList.add('hidden');
loadMenu.classList.add('hidden');
boardContainer.classList.add('hidden');
pauseButton.classList.add('paused');
pauseMenu.classList.remove('hidden');
}
function displayGameBoard() {
mainMenu.classList.add('hidden');
loadMenu.classList.add('hidden');
pauseMenu.classList.add('hidden');
gameOverMenu.classList.add('hidden');
pauseButton.classList.remove('paused');
buttonContainer.classList.remove('hidden');
boardContainer.classList.remove('hidden');
}
function displayGameOver() {
mainMenu.classList.add('hidden');
loadMenu.classList.add('hidden');
boardContainer.classList.add('hidden');
pauseButton.classList.remove('paused');
buttonContainer.classList.add('hidden');
gameOverMenu.classList.remove('hidden');
}

28
public/script/element.js Normal file
View File

@ -0,0 +1,28 @@
let board = document.getElementById('tetris-canvas');
const boardBis = document.getElementById('tetris-canvas').cloneNode(true);
const scoreElement = document.getElementById('score-value');
const loadMenu = document.getElementById('load-menu');
const mainMenu = document.getElementById('main-menu');
const pauseMenu = document.getElementById('pause-menu');
const startButton = document.getElementById('start-button');
const pauseButton = document.getElementById('pause-button');
let preview = document.getElementById('preview-canvas');
const previewBis = document.getElementById('preview-canvas').cloneNode(true);
const gameOverMenu = document.getElementById('game-over-menu');
const restartButton = document.getElementById('restart-button');
const boardContainer = document.getElementById('board-container');
const previewContainer = document.getElementById('preview-container');
const buttonContainer = document.getElementById('pause-container');
const highScoreTemplate = document.getElementById('high-score-template');
const highScoresList = document.getElementById('high-scores-list');
const resumeButton = document.getElementById('resume-button');
const mainMenuButton = document.getElementById('main-menu-button');
const mainMenuButtonBis = document.getElementById('main-menu-button2');

View File

@ -0,0 +1,363 @@
function ifPieceInBoard(x, y) {
let board = game.board;
if (x < 0 || x >= width || y >= height) {
return true;
}
for (let i = 0; i < board.length; i++) {
if (board[i].x === x && board[i].y === y) {
return true;
}
}
return false;
}
function ifMovePiece(x, y, piece = game.currentPiece) {
for (let i = 0; i < piece.squares.length; i++) {
let square = piece.squares[i];
let newX = square.x + x;
let newY = square.y + y;
if (ifPieceInBoard(newX, newY)) {
return false;
}
}
return true;
}
function ifMoveDown(piece = game.currentPiece) {
return ifMovePiece(0, 1, piece);
}
function ifMoveLeft(piece = game.currentPiece) {
return ifMovePiece(-1, 0, piece);
}
function ifMoveRight(piece = game.currentPiece) {
return ifMovePiece(1, 0, piece);
}
function ifRotate(piece = game.currentPiece) {
let centerX = piece.xCenter;
let centerY = piece.yCenter;
for (let i = 0; i < piece.squares.length; i++) {
let square = piece.squares[i];
let x = square.x - centerX;
let y = square.y - centerY;
let newX = -y + centerX;
let newY = x + centerY;
if (ifPieceInBoard(newX, newY)) {
return false;
}
}
return true;
}
function moveDown(piece = game.currentPiece) {
for (let i = 0; i < piece.squares.length; i++) {
let square = piece.squares[i];
square.y += 1;
}
piece.yCenter += 1;
}
function moveLeft(piece = game.currentPiece) {
if (!ifMoveLeft(piece)) {
return;
}
for (let i = 0; i < piece.squares.length; i++) {
let square = piece.squares[i];
square.x -= 1;
}
piece.xCenter -= 1;
}
function moveRight(piece = game.currentPiece) {
if (!ifMoveRight(piece)) {
return;
}
for (let i = 0; i < piece.squares.length; i++) {
let square = piece.squares[i];
square.x += 1;
}
piece.xCenter += 1;
}
function ifCantRotate(piece, game) {
let centerX = piece.xCenter;
let centerY = piece.yCenter;
let moveX = [0, -1, 1, -2, 2];
let moveY = [0, -1];
for (let i = 0; i < moveX.length; i++) {
for (let j = 0; j < moveY.length; j++) {
let pieceBis = clonePiece(piece);
pieceBis.xCenter += moveX[i];
pieceBis.yCenter += moveY[j];
for (let k = 0; k < pieceBis.squares.length; k++) {
let square = pieceBis.squares[k];
square.x += moveX[i];
square.y += moveY[j];
}
if (ifRotate(pieceBis, game)) {
return [false, moveX[i], moveY[j]];
}
}
}
return [true, centerX, centerY];
}
function rotatePiece(piece) {
if (!ifRotate(piece, game)) {
let info = ifCantRotate(piece, game);
let canRotate = info[0];
let x = info[1];
let y = info[2];
if (canRotate) {
return;
}
for (let i = 0; i < piece.squares.length; i++) {
let square = piece.squares[i];
square.x += x;
square.y += y;
}
piece.xCenter += x;
piece.yCenter += y;
}
let centerX = piece.xCenter;
let centerY = piece.yCenter;
for (let i = 0; i < piece.squares.length; i++) {
let square = piece.squares[i];
let x = square.x - centerX;
let y = square.y - centerY;
square.x = -y + centerX;
square.y = x + centerY;
}
}
function fixPieceToBoard(piece = game.currentPiece) {
for (let i = 0; i < piece.squares.length; i++) {
let square = piece.squares[i];
game.board.push(square);
}
opacityCurrentPiece = 1;
checkLine();
// Vous pouvez également gérer ici la création d'une nouvelle pièce
// ou vérifier si des lignes doivent être supprimées.
}
function checkLine(ifNoFall = false) {
let lineFilled = [];
for (let i = 0; i < 20; i++) {
let filled = true;
for (let j = 0; j < 10; j++) {
if (!ifPieceInBoard(j, i)) {
filled = false;
break;
}
}
if (filled) {
lineFilled.push(i);
}
}
for (let i = 0; i < game.board.length; i++) {
let square = game.board[i];
let y = square.y;
if (!ifNoFall) {
for (let j = 0; j < lineFilled.length; j++) {
if (lineFilled[j] > y) {
square.y += 1;
}
}
}
if (lineFilled.includes(y)) {
game.board.splice(i, 1);
i--;
}
}
if (lineFilled.length == 1) {
game.score += 40;
}
else if (lineFilled.length == 2) {
game.score += 100;
}
else if (lineFilled.length == 3) {
game.score += 300;
}
else if (lineFilled.length == 4) {
game.score += 1200;
}
game.lines += lineFilled.length;
game.level = Math.floor(game.lines / 10);
}
function switchPiece() {
game.currentPiece = game.nextPiece;
game.nextPiece = getRandomPiece();
for (let i = 0; i < game.currentPiece.squares.length; i++) {
let x = game.currentPiece.squares[i].x;
let y = game.currentPiece.squares[i].y;
if (ifPieceInBoard(x, y)) {
finishGame();
return;
}
}
}
function finishGame(sendScore = true, gameOver = true) {
game.gameOver = true;
clearInterval(dropInterval);
clearInterval(leftInterval);
clearInterval(rightInterval);
clearInterval(refreshInterval);
clearInterval(endDropInterval);
endDropInterval = null;
opacityCurrentPiece = 1;
game.currentPiece = null;
dropInterval = null;
refreshInterval = null;
if (gameOver) {
displayGameOver();
} else {
displayMainMenu();
}
if (sendScore) {
const playerName = prompt('Enter your name:');
sendScore(game, playerName);
}
}
async function sendScore(playerName) {
if (playerName) {
const data = {
score: game.score,
lines: game.lines,
playerName
};
console.log(data);
try {
// Send a PUT request to the server
const query = await fetch('/api/sendScore', {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
// Check if the response is ok before parsing
if (!query.ok) {
throw new Error(`Server returned ${query.status}: ${query.statusText}`);
}
// Check the content type to ensure it's JSON
const contentType = query.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
throw new Error('Server did not return JSON');
}
// Get the response body
const response = await query.json();
if (response.success) {
console.log('Score and history saved successfully');
}
else {
console.error('Error saving score and history:', response.error);
}
} catch (error) {
console.error('Failed to save score:', error.message);
alert('Could not save your score. Please try again later.');
}
}
}
function setSpeed() {
if (game.level > 29) {
game.speed = (speedSecondsToBottomPerLevel[29] / 20) * 1000;
}
else {
game.speed = (speedSecondsToBottomPerLevel[game.level] / 20) * 1000;
}
}
function endDrop() {
let piece = game.currentPiece;
// La pièce ne peut plus descendre, elle est fixée sur le plateau
if (!endDropInterval) { // Vérifiez qu'il n'y a pas déjà un intervalle actif
endDropInterval = setInterval(() => {
opacityCurrentPiece -= 0.1;
if (opacityCurrentPiece <= 0) {
fixPieceToBoard(piece, game);
clearInterval(endDropInterval);
endDropInterval = null; // Réinitialisez après l'arrêt
opacityCurrentPiece = 1;
switchPiece(game);
setSpeed(game);
initOrChangeDropInterval(game.speed);
}
}, 60);
}
}
function fastDrop(piece = game.currentPiece) {
if (piece) {
let canMoveDown = true;
while (canMoveDown) {
canMoveDown = ifMoveDown();
if (canMoveDown) {
moveDown();
}
}
fixPieceToBoard();
clearInterval(endDropInterval);
endDropInterval = null; // Réinitialisez après l'arrêt
opacityCurrentPiece = 1;
setSpeed(game);
initOrChangeDropInterval(game.speed);
switchPiece(game);
}
}

View File

@ -1,17 +1,48 @@
const board = document.getElementById('tetris-canvas'); let board = document.getElementById('tetris-canvas');
const boardBis = document.getElementById('tetris-canvas').cloneNode(true);
const scoreElement = document.getElementById('score-value'); const scoreElement = document.getElementById('score-value');
const loadMenu = document.getElementById('load-menu'); const loadMenu = document.getElementById('load-menu');
const mainMenu = document.getElementById('main-menu'); const mainMenu = document.getElementById('main-menu');
const pauseMenu = document.getElementById('pause-menu'); const pauseMenu = document.getElementById('pause-menu');
const startButton = document.getElementById('start-button'); const startButton = document.getElementById('start-button');
const pauseButton = document.getElementById('pause-button');
let preview = document.getElementById('preview-canvas');
const previewBis = document.getElementById('preview-canvas').cloneNode(true);
const gameOverMenu = document.getElementById('game-over-menu');
const restartButton = document.getElementById('restart-button');
const boardContainer = document.getElementById('board-container');
const previewContainer = document.getElementById('preview-container');
const buttonContainer = document.getElementById('pause-container');
const highScoreTemplate = document.getElementById('high-score-template');
const highScoresList = document.getElementById('high-scores-list');
restartButton.addEventListener('click', () =>
{
displayGameBoard();
startGame();
refresh();
}
);
startButton.addEventListener('click', () => startButton.addEventListener('click', () =>
{ {
startGame(); startGame();
}); });
const boardContext = board.getContext('2d'); pauseButton.addEventListener('click', () =>
{
pauseGame();
});
let boardContext = board.getContext('2d');
let previewContext = preview.getContext('2d');
boardContext.fillStyle = 'black'; boardContext.fillStyle = 'black';
boardContext.fillRect(0, 0, board.width, board.height); boardContext.fillRect(0, 0, board.width, board.height);
@ -36,6 +67,9 @@ let level = 0;
let lines = 0; let lines = 0;
let speed = 1000; let speed = 1000;
let pause = false;
let gameOver = true;
const templatePiece = [ const templatePiece = [
[ // ok [ // ok
[3, 0], [3, 0],
@ -88,16 +122,14 @@ const templatePiece = [
] ]
]; ];
const templateColor = [ const templateColor = [
[255, 0, 0, 255], [240, 87, 84, 255],
[0, 255, 0, 255], [254, 223, 92, 255],
[0, 0, 255, 255], [27, 148, 118, 255],
[255, 255, 0, 255], [36, 115, 155, 255],
[0, 255, 255, 255], [106, 190, 178, 255],
[255, 0, 255, 255], [164, 199, 218, 255],
[255, 40, 130, 255] [177, 225, 218, 255]
]; ];
const speedSecondsToBotomPerLevel = [ const speedSecondsToBotomPerLevel = [
@ -134,7 +166,9 @@ const speedSecondsToBotomPerLevel = [
]; ];
let currentPiece = []; let currentPiece = [];
let color = [] let nextPiece = [];
let color = [];
let nextColor = [];
let boardData = []; let boardData = [];
@ -144,14 +178,19 @@ const MOVEDOWN = 3;
const ROTATE = 4; const ROTATE = 4;
const MOVEDOWNFAST = 5; const MOVEDOWNFAST = 5;
const RESPAWN = 6; const RESPAWN = 6;
const CHANGENEXTPIECE = 7;
let history = []; let history = [];
function startGame() { function startGame() {
pause = false;
gameOver = false;
boardData = []; boardData = [];
currentPiece = []; currentPiece = [];
color = []; color = [];
history = []; history = [];
keyPress = [];
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++) {
boardData[i] = []; boardData[i] = [];
@ -166,20 +205,33 @@ function startGame() {
speed = 1000; speed = 1000;
scoreElement.innerText = score; scoreElement.innerText = score;
mainMenu.classList.add('hidden'); displayGameBoard();
loadMenu.classList.add('hidden');
board.classList.add('hidden');
board.classList.remove('hidden');
iniGame(); iniGame();
refresh(); refresh();
} }
function pauseGame() {
if (gameOver) {
return;
}
if (pause) {
pause = false;
displayGameBoard();
//initAndChangeSpeedDrop();
} else {
pause = true;
displayPause();
clearDownInterval();
clearInterval(endDownInterval);
}
}
function iniGame() { function iniGame() {
// Initialisation de la vitesse de chute // Initialisation de la vitesse de chute
speed = (speedSecondsToBotomPerLevel[level]/20) * 1000; speed = (speedSecondsToBottomPerLevel[level]/20) * 1000;
spawnPiece(); spawnPiece();
@ -191,77 +243,6 @@ function iniGame() {
}, speed); }, speed);
} }
function drawTetisWithOppacity(x, y, color, oppacity) {
boardContext.drawImage(img, x, y, board.width/10, board.width/10);
const imageData = boardContext.getImageData(x, y, board.width/10, board.width/10);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
data[i] = data[i] * color[0] / 255;
data[i + 1] = data[i + 1] * color[1] / 255;
data[i + 2] = data[i + 2] * color[2] / 255;
data[i + 3] = data[i + 3] * oppacity / 255;
}
boardContext.putImageData(imageData, x, y);
}
function drawTetris(x, y, color) {
drawTetisWithOppacity(x, y, color, 255);
}
function drawCurrentPiece() {
for (let i = 0 ; i < currentPiece.length - 1; i++) {
let piece = currentPiece[i];
drawTetisWithOppacity(piece[0] * board.width/10, piece[1] * board.width/10, color, oppacity);
}
}
function drawBoard() {
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 20; j++) {
if (!(boardData[i][j][0] == 0 && boardData[i][j][1] == 0 && boardData[i][j][2] == 0)) {
drawTetris(i * board.width/10, j * board.width/10, boardData[i][j]);
}
}
}
}
function clearBoard() {
boardContext.fillStyle = 'black';
boardContext.fillRect(0, 0, board.width, board.height);
}
function drawGrid() {
boardContext.strokeStyle = 'gray'; // Couleur des lignes du quadrillage
boardContext.lineWidth = 0.5; // Épaisseur des lignes
// Dessiner les lignes verticales
for (let x = 0; x <= board.width; x += board.width / 10) {
boardContext.beginPath();
boardContext.moveTo(x, 0);
boardContext.lineTo(x, board.height);
boardContext.stroke();
}
// Dessiner les lignes horizontales
for (let y = 0; y <= board.height; y += board.height / 20) {
boardContext.beginPath();
boardContext.moveTo(0, y);
boardContext.lineTo(board.width, y);
boardContext.stroke();
}
}
function refresh() {
clearBoard();
drawGrid();
drawBoard();
drawCurrentPiece();
}
function loadTetris() { function loadTetris() {
clearDownInterval(); clearDownInterval();
clearInterval(endDownInterval); clearInterval(endDownInterval);
@ -270,6 +251,8 @@ function loadTetris() {
currentPieceToBoard(); currentPieceToBoard();
checkLine(); checkLine();
spawnPiece(); spawnPiece();
//checkEndDrop();
} }
function currentPieceToBoard() { function currentPieceToBoard() {
@ -282,18 +265,100 @@ function currentPieceToBoard() {
} }
function spawnPiece() { function spawnPiece() {
const random = Math.floor(Math.random() * templatePiece.length); let random = 0;
currentPiece = []; if (nextPiece.length == 0) {
for (let i = 0; i < templatePiece[random].length; i++) { random = Math.floor(Math.random() * templatePiece.length);
currentPiece.push([templatePiece[random][i][0], templatePiece[random][i][1]]);
for (let i = 0; i < templatePiece[random].length; i++) {
nextPiece.push([templatePiece[random][i][0], templatePiece[random][i][1]]);
}
nextColor = templateColor[random];
history.push([CHANGENEXTPIECE, random]);
}
currentPiece = nextPiece;
if (gameOver) {
return;
} }
color = templateColor[random]; for (let i = 0; i < currentPiece.length - 1; i++) {
if (boardData[currentPiece[i][0]][currentPiece[i][1]][0] != 0 &&
boardData[currentPiece[i][0]][currentPiece[i][1]][1] != 0 &&
boardData[currentPiece[i][0]][currentPiece[i][1]][2] != 0) {
history.push([RESPAWN, random]); gameOver = true;
clearDownInterval();
clearInterval(endDownInterval);
oppacity = 255;
displayGameOver();
sendScore();
return;
}
}
random = Math.floor(Math.random() * templatePiece.length);
nextPiece = [];
for (let i = 0; i < templatePiece[random].length; i++) {
nextPiece.push([templatePiece[random][i][0], templatePiece[random][i][1]]);
}
history.push([CHANGENEXTPIECE, random]);
color = nextColor;
nextColor = templateColor[random];
} }
async function sendScore() {
const playerName = prompt('Enter your name:');
if (playerName) {
const data = {
score,
history,
playerName
};
console.log(data);
try {
// Send a PUT request to the server
const query = await fetch('/api/sendScore', {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
// Check if the response is ok before parsing
if (!query.ok) {
throw new Error(`Server returned ${query.status}: ${query.statusText}`);
}
// Check the content type to ensure it's JSON
const contentType = query.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) {
throw new Error('Server did not return JSON');
}
// Get the response body
const response = await query.json();
if (response.success) {
console.log('Score and history saved successfully');
}
else {
console.error('Error saving score and history:', response.error);
}
} catch (error) {
console.error('Failed to save score:', error.message);
alert('Could not save your score. Please try again later.');
}
}
}
function ifMoveTo(x, y) { function ifMoveTo(x, y) {
return canMoveTo(x, y, currentPiece); return canMoveTo(x, y, currentPiece);
@ -360,7 +425,7 @@ function rotate() {
} }
else { else {
for (let x = -1; x <= 1; x++) { for (let x = -1; x <= 1; x++) {
for (let y = -1; y <= 1; y++) { for (let y = 0; y >= -1; y--) {
if (canMoveTo(x, y, currentPieceCopy)) { if (canMoveTo(x, y, currentPieceCopy)) {
moveToPiece(x, y, currentPieceCopy); moveToPiece(x, y, currentPieceCopy);
currentPiece = currentPieceCopy; currentPiece = currentPieceCopy;
@ -368,7 +433,7 @@ function rotate() {
history.push([ROTATE, 0]); history.push([ROTATE, 0]);
x = 2; x = 2;
y = 2; y = -2;
} }
} }
} }
@ -402,7 +467,6 @@ function checkEndDrop() {
} }
} }
function clearDownInterval() { function clearDownInterval() {
clearInterval(downInterval); clearInterval(downInterval);
downInterval = null; downInterval = null;
@ -457,10 +521,10 @@ function checkLine() {
function initAndChangeSpeedDrop() { function initAndChangeSpeedDrop() {
if (level > 29) { if (level > 29) {
speed = (speedSecondsToBotomPerLevel[29]/20) * 1000; speed = (speedSecondsToBottomPerLevel[29]/20) * 1000;
} }
else { else {
speed = (speedSecondsToBotomPerLevel[level]/20) * 1000; speed = (speedSecondsToBottomPerLevel[level]/20) * 1000;
} }
clearDownInterval(); clearDownInterval();
downInterval = setInterval(() => { downInterval = setInterval(() => {
@ -518,7 +582,7 @@ function moveRight() {
return true; return true;
} }
function fasteDrop() { function fastDrop() {
while (ifMoveTo(0, 1)) { while (ifMoveTo(0, 1)) {
moveDown(); moveDown();
} }
@ -528,20 +592,30 @@ function fasteDrop() {
oppacity = 255; oppacity = 255;
loadTetris(); loadTetris();
history.push([MOVEDOWNFAST, 0]);
} }
document.addEventListener('keydown', (event) => { document.addEventListener('keydown', (event) => {
if (event.key == 'ArrowUp' && !keyPress.includes('ArrowUp')) { if (event.key == 'Escape' && !keyPress.includes('Escape')) {
pauseGame();
return;
}
if (pause) {
return;
}
if (gameOver) {
return;
}
if ((event.key == 'ArrowUp' || event.key == 'w' || event.key == 'z') && !keyPress.includes('ArrowUp') && !keyPress.includes('w') && !keyPress.includes('z')) {
rotate(); rotate();
} else if (event.key == 'ArrowDown' && !keyPress.includes('ArrowDown')) { } else if ((event.key == 'ArrowDown' || event.key == 's') && !keyPress.includes('ArrowDown') && !keyPress.includes('s')) {
clearDownInterval(); clearDownInterval();
downInterval = setInterval(() => { downInterval = setInterval(() => {
moveDown(); moveDown();
refresh(); refresh();
}, 50); }, 50);
} else if (event.key == 'ArrowLeft' && !keyPress.includes('ArrowLeft')) { } else if ((event.key == 'ArrowLeft' || event.key == 'a' || event.key == 'q') && !keyPress.includes('ArrowLeft') && !keyPress.includes('q') && !keyPress.includes('a')) {
moveLeft(); moveLeft();
leftInterval = setInterval(() => { leftInterval = setInterval(() => {
@ -549,7 +623,7 @@ document.addEventListener('keydown', (event) => {
refresh(); refresh();
}, 100); }, 100);
} else if (event.key == 'ArrowRight' && !keyPress.includes('ArrowRight')) { } else if (((event.key == 'ArrowRight') || event.key == 'd') && !keyPress.includes('ArrowRight') && !keyPress.includes('d')) {
moveRight(); moveRight();
rightInterval = setInterval(() => { rightInterval = setInterval(() => {
@ -557,7 +631,7 @@ document.addEventListener('keydown', (event) => {
refresh(); refresh();
}, 100); }, 100);
} else if (event.key == ' ' && !keyPress.includes(' ')) { } else if (event.key == ' ' && !keyPress.includes(' ')) {
fasteDrop(); fastDrop();
} }
refresh(); refresh();
@ -568,12 +642,14 @@ document.addEventListener('keydown', (event) => {
}); });
document.addEventListener('keyup', (event) => { document.addEventListener('keyup', (event) => {
if (event.key == 'ArrowDown') { if (!pause && !gameOver) {
initAndChangeSpeedDrop(); if (event.key == 'ArrowDown' || event.key == 's') {
} else if (event.key == 'ArrowLeft') { initAndChangeSpeedDrop();
clearInterval(leftInterval); } else if (event.key == 'ArrowLeft' || event.key == 'a' || event.key == 'q') {
} else if (event.key == 'ArrowRight') { clearInterval(leftInterval);
clearInterval(rightInterval); } else if (event.key == 'ArrowRight' || event.key == 'd') {
clearInterval(rightInterval);
}
} }
keyPress = keyPress.filter((key) => { keyPress = keyPress.filter((key) => {
@ -582,8 +658,5 @@ document.addEventListener('keyup', (event) => {
}); });
img.onload = () => { img.onload = () => {
loadMenu.classList.add('hidden'); displayMainMenu();
mainMenu.classList.remove('hidden');
} }

View File

@ -0,0 +1,42 @@
const speedSecondsToBottomPerLevel = [
15.974, //0
14.31, //1
12.646, //2
10.982, //3
9.318, //4
7.654, //5
5.99, //6
4.326, //7
2.662, //8
1.997, //9
1.664, //10
1.664, //11
1.664, //12
1.331, //13
1.331, //14
1.331, //15
0.998, //16
0.998, //17
0.998, //18
0.666, //19
0.666, //20
0.666, //21
0.666, //22
0.666, //23
0.666, //24
0.666, //25
0.666, //26
0.666, //27
0.666, //28
0.333 //29
];
const width = 10;
const height = 20;
const DOWNHISTORY = 1;
const LEFTHISTORY = 2;
const RIGHTHISTORY = 3;
const ROTATEHISTORY = 4;
const ENDPARTYHISTORY = 5;
const ISNEXTHISTORY = 6;

View File

@ -0,0 +1,18 @@
startButton.addEventListener('click', function() {
initGame();
});
pauseButton.addEventListener('click', function() {
pauseGame();
});
restartButton.addEventListener('click', function() {
initGame();
});
resumeButton.addEventListener('click', function() {
pauseGame();
});
mainMenuButton.addEventListener('click', function() {
finishGame(false, false);
});
mainMenuButtonBis.addEventListener('click', function() {
finishGame(false, false);
});

13
public/script/var.js Normal file
View File

@ -0,0 +1,13 @@
let game = null;
let keyPress = [];
let dropInterval = null;
let leftInterval = null;
let rightInterval = null;
let refreshInterval = null;
let endDropInterval = null;
let opacityCurrentPiece = 1;
let history = [];

218
public/script/view.js Normal file
View File

@ -0,0 +1,218 @@
let gapPiece = 6;
function drawTetisWithOppacity(x, y, color, oppacity) {
boardContext.fillStyle = '#303f4f'; // Couleur de fond
boardContext.fillRect(x, y, board.width/10, board.width/10);
boardContext.fillStyle = `rgba(${color[0]}, ${color[1]}, ${color[2]}, ${oppacity/255})`
boardContext.fillRect(x + gapPiece/2, y + gapPiece/2, board.width/10 - gapPiece, board.width/10 - gapPiece);
}
function drawTetrisOnThePreview(x, y, color) {
previewContext.fillStyle = '#303f4f'; // Couleur de fond
previewContext.fillRect(x, y, preview.width/5, preview.width/5);
previewContext.fillStyle = `rgba(${color[0]}, ${color[1]}, ${color[2]}, ${oppacity})`
previewContext.fillRect(x + gapPiece/2, y + gapPiece/2, preview.width/5 - gapPiece, preview.width/5 - gapPiece);
}
function drawTetris(x, y, color) {
drawTetisWithOppacity(x, y, color, 255);
}
function drawCurrentPiece() {
for (let i = 0 ; i < currentPiece.length - 1; i++) {
let piece = currentPiece[i];
drawTetisWithOppacity(piece[0] * board.width/10, piece[1] * board.width/10, color, oppacity);
}
}
function drawHologram() {
let hologramPiece = [];
for (let i = 0; i < currentPiece.length; i++) {
let piece = currentPiece[i];
hologramPiece.push([piece[0], piece[1]]);
}
while (canMoveTo(0, 1, hologramPiece)) {
moveToPiece(0, 1, hologramPiece);
}
for (let i = 0; i < hologramPiece.length - 1; i++) {
let piece = hologramPiece[i];
drawTetisWithOppacity(piece[0] * board.width/10, piece[1] * board.width/10, color, 120);
}
}
function drawGrid() {
boardContext.strokeStyle = 'gray'; // Couleur des lignes du quadrillage
boardContext.lineWidth = 0.5; // Épaisseur des lignes
// Dessiner les lignes verticales
for (let x = 0; x <= board.width; x += board.width / 10) {
boardContext.beginPath();
boardContext.moveTo(x, 0);
boardContext.lineTo(x, board.height);
boardContext.stroke();
}
// Dessiner les lignes horizontales
for (let y = 0; y <= board.height; y += board.height / 20) {
boardContext.beginPath();
boardContext.moveTo(0, y);
boardContext.lineTo(board.width, y);
boardContext.stroke();
}
}
function drawBoard() {
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 20; j++) {
if (!(boardData[i][j][0] == 0 && boardData[i][j][1] == 0 && boardData[i][j][2] == 0)) {
drawTetris(i * board.width/10, j * board.width/10, boardData[i][j]);
}
}
}
}
function clearBoard() {
// Ajouter un motif subtil en arrière-plan
boardContainer.innerHTML = '';
board = boardBis.cloneNode(true);
boardContainer.appendChild(board);
boardContext = board.getContext('2d');
for (let i = 0; i < 10; i++) {
for (let j = 0; j < 20; j++) {
if ((i + j) % 2 === 0) {
boardContext.fillStyle = '#6d728020'; // Encore plus sombre
}
else {
boardContext.fillStyle = '#3d425020'; // Presque noir-bleuté
}
boardContext.fillRect(
i * board.width/10,
j * board.height/20,
board.width/10,
board.height/20
);
}
}
}
function displayPreview() {
previewContainer.innerHTML = '';
preview = previewBis.cloneNode(true);
previewContainer.appendChild(preview);
previewContext = preview.getContext('2d');
// make damier
for (let x = 0; x < 5 ; x++) {
for (let y = 0;y < 5; y++) {
if ((x + y) % 2 === 0) {
previewContext.fillStyle = '#6d728020'; // Encore plus sombre
}
else {
previewContext.fillStyle = '#3d425020'; // Presque noir-bleuté
}
previewContext.fillRect(
x * preview.width/5,
y * preview.height/5,
preview.width/5,
preview.height/5
);
}
}
let xDif = nextPiece[nextPiece.length - 1][0] - 2;
let yDif = nextPiece[nextPiece.length - 1][1] + 0.5;
for (let i = 0; i < nextPiece.length - 1; i++) {
let piece = nextPiece[i];
drawTetrisOnThePreview((piece[0] - xDif) * preview.width/5, (piece[1] + yDif) * preview.width/5, nextColor);
}
}
function refresh() {
clearBoard();
drawGrid();
drawBoard();
drawHologram();
drawCurrentPiece();
displayPreview();
}
function displayMainMenu() {
mainMenu.classList.remove('hidden');
loadMenu.classList.add('hidden');
boardContainer.classList.add('hidden');
pauseButton.classList.remove('paused');
buttonContainer.classList.add('hidden');
gameOverMenu.classList.add('hidden');
}
function displayPause() {
mainMenu.classList.add('hidden');
loadMenu.classList.add('hidden');
boardContainer.classList.add('hidden');
pauseButton.classList.add('paused');
pauseMenu.classList.remove('hidden');
}
function displayGameBoard() {
mainMenu.classList.add('hidden');
loadMenu.classList.add('hidden');
pauseMenu.classList.add('hidden');
gameOverMenu.classList.add('hidden');
pauseButton.classList.remove('paused');
buttonContainer.classList.remove('hidden');
boardContainer.classList.remove('hidden');
}
function displayGameOver() {
mainMenu.classList.add('hidden');
loadMenu.classList.add('hidden');
boardContainer.classList.add('hidden');
pauseButton.classList.remove('paused');
buttonContainer.classList.add('hidden');
gameOverMenu.classList.remove('hidden');
}
let intervalDisplayScoreBoard = null;
displayScoreBoard();
function displayScoreBoard() {
if (intervalDisplayScoreBoard) {
clearInterval(intervalDisplayScoreBoard);
intervalDisplayScoreBoard = null;
}
intervalDisplayScoreBoard = setInterval(() => {
fetch('/api/getHighScores')
.then(response => response.json())
.then(data => {
highScoresList.innerHTML = '';
data.forEach(score => {
// Clone template content properly
const scoreElement = document.importNode(highScoreTemplate.content, true).firstElementChild;
scoreElement.querySelector('.high-score-name').innerHTML = score.player_name;
scoreElement.querySelector('.high-score-score').innerHTML = score.score;
highScoresList.appendChild(scoreElement);
});
})
.catch(error => console.error('Error fetching high scores:', error));
}, 1000);
}

View File

@ -1,107 +1,249 @@
@font-face {
font-family: 'PixelatedElegance';
src: url('../font/Pixeboy-z8XGD.ttf') format('truetype');
}
body { body {
margin: 0; margin: 0;
display: flex; display: flex;
height: 100svh; height: 100svh;
place-items: center; place-items: center;
background: repeating-conic-gradient( background: repeating-conic-gradient(from 45deg,
from 45deg, #7FB196 0% 25%,
#253a5e 0% 25%, #3C425A 0% 50%);
#3c5e8b 0% 50% background-size: max(10vw, 10svh) max(10vw, 10svh);
);
background-size: max(10vw, 10svh) max(10vw, 10svh);
background-color: #3c5e8b;
font-family: 'Roboto', sans-serif; font-family: 'Roboto', sans-serif;
align-self: center; align-self: center;
justify-content: center; justify-content: center;
flex-direction: row; flex-direction: row;
gap: 1rem; justify-content: space-evenly;
gap: 1rem;
font-family: 'PixelatedElegance', monospace;
font-size: 1.5rem;
} }
.container { .container {
display: flex; gap: 1rem;
flex-direction: row; display: flex;
flex-direction: row;
} }
.tetris { .tetris {
width: 300px; width: 300px;
height: 600px; height: 600px;
}
background: black; .frame {
padding: 10px;
margin: 0px;
border-radius: 10px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.6);
background: #414241a0;
position: relative;
/* Nécessaire pour le positionnement du pseudo-élément */
p {
padding: 0px;
margin: 0px;
}
}
.frame::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #4E4F4DA0;
border-radius: 10px;
/* Même border-radius que le parent */
backdrop-filter: blur(8px);
-webkit-backdrop-filter: blur(8px);
/* Pour Safari */
z-index: -1;
/* Assure que le fond est derrière le contenu */
} }
.first { .first {
width: 150px; display: flex;
flex-direction: column;
background: white; gap: 1rem;
align-items: end;
width: 100px;
} }
.last { .last {
width: 150px; display: flex;
flex-direction: column;
background: white; gap: 1rem;
align-items: start;
width: 100px;
justify-content: space-between;
} }
.main-menu { .main-menu {
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1rem; gap: 1rem;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
background-color: black;
} }
.load-menu { .load-menu {
height: 100%; height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1rem; gap: 1rem;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
color: white; color: white;
}
.icon.play-pause {
border: 0;
background: transparent;
box-sizing: border-box;
width: 0;
height: 37px;
/* Réduit de moitié (était 74px) */
border-color: transparent transparent transparent #74dc74;
transition: 100ms all ease;
cursor: pointer;
border-style: solid;
border-width: 18.5px 0 18.5px 30px;
/* Réduit proportionnellement (était 37px 0 37px 60px) */
&.paused {
border-style: double;
border-width: 0px 0 0px 30px;
/* Réduit (était 60px) */
border-color: transparent transparent transparent #ff5e5e;
}
}
.icon.play-pause.play::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.pauseContainer {
width: min-content;
height: min-content;
} }
.loader { .loader {
width: 48px; width: 48px;
height: 48px; height: 48px;
border: 5px solid #FFF; border: 5px solid #FFF;
border-radius: 50%; border-radius: 50%;
display: inline-block; display: inline-block;
box-sizing: border-box; box-sizing: border-box;
position: relative; position: relative;
animation: pulse 1s linear infinite; animation: pulse 1s linear infinite;
} }
.loader:after {
content: ''; .loader:after {
position: absolute; content: '';
width: 48px; position: absolute;
height: 48px; width: 48px;
border: 5px solid #FFF; height: 48px;
border-radius: 50%; border: 5px solid #FFF;
display: inline-block; border-radius: 50%;
box-sizing: border-box; display: inline-block;
left: 50%; box-sizing: border-box;
top: 50%; left: 50%;
transform: translate(-50%, -50%); top: 50%;
animation: scaleUp 1s linear infinite; transform: translate(-50%, -50%);
} animation: scaleUp 1s linear infinite;
}
@keyframes scaleUp {
0% { transform: translate(-50%, -50%) scale(0) } @keyframes scaleUp {
60% , 100% { transform: translate(-50%, -50%) scale(1)} 0% {
} transform: translate(-50%, -50%) scale(0)
@keyframes pulse { }
0% , 60% , 100%{ transform: scale(1) }
80% { transform: scale(1.2)} 60%,
} 100% {
transform: translate(-50%, -50%) scale(1)
.hidden { }
display: none; }
@keyframes pulse {
0%,
60%,
100% {
transform: scale(1)
}
80% {
transform: scale(1.2)
}
}
.scoreboard {
width: 200px;
height: 600px;
display: flex;
flex-direction: column;
gap: 1rem;
align-items: center;
}
.options {
width: 200px;
height: 600px;
}
.button {
background: #4E4F4DA0;
border-radius: 10px;
padding: 10px;
margin: 0px;
cursor: pointer;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.6);
display: flex;
flex-direction: column;
gap: 1rem;
align-items: center;
}
.menu {
display: flex;
flex-direction: column;
gap: 1rem;
align-items: center;
justify-content: center;
height: 100%;
width: 100%;
color: white;
}
.hidden {
display: none;
} }