From b04402259fe3353fa19485001dc8af5118887a61 Mon Sep 17 00:00:00 2001
From: "julien.bouin.etu" <julien.bouin.etu@univ-lille.fr>
Date: Sat, 6 Apr 2024 22:08:27 +0200
Subject: [PATCH] debut refactor

---
 client/src/afficher.js                    |   2 +-
 client/src/main.js                        | 193 ++++--------
 client/src/render.js                      |   2 +-
 {client/src => common}/Coordinate.js      |   0
 {client/src => common}/avatar.js          |   6 +-
 {client/src => common}/avatar.test.js     |   0
 {client/src => common}/utils.js           |   0
 {client/src => server}/Projectile.js      |   2 +-
 server/bonus.js                           |   4 +-
 server/enemis.js                          |   4 +-
 {client/src => server}/entite.js          |   0
 server/gameLogic.js                       | 144 +++++++++
 {client/src => server}/hitbox.js          |   0
 {client/src => server}/hitbox.test.js     |   0
 server/index.js                           | 356 ++++++++++++----------
 {client/src => server}/projectile.test.js |   0
 16 files changed, 412 insertions(+), 301 deletions(-)
 rename {client/src => common}/Coordinate.js (100%)
 rename {client/src => common}/avatar.js (95%)
 rename {client/src => common}/avatar.test.js (100%)
 rename {client/src => common}/utils.js (100%)
 rename {client/src => server}/Projectile.js (92%)
 rename {client/src => server}/entite.js (100%)
 create mode 100644 server/gameLogic.js
 rename {client/src => server}/hitbox.js (100%)
 rename {client/src => server}/hitbox.test.js (100%)
 rename {client/src => server}/projectile.test.js (100%)

diff --git a/client/src/afficher.js b/client/src/afficher.js
index 9a221f2..540067c 100644
--- a/client/src/afficher.js
+++ b/client/src/afficher.js
@@ -1,5 +1,5 @@
 import setHtml from './setHtml.js';
-import { Coordinate } from './Coordinate.js';
+import { Coordinate } from '../../common/Coordinate.js';
 
 export default class Afficher {
 	gameStarted;
diff --git a/client/src/main.js b/client/src/main.js
index ca2d2e1..142b376 100644
--- a/client/src/main.js
+++ b/client/src/main.js
@@ -1,7 +1,7 @@
-import { Avatar } from './avatar.js';
+import { Avatar } from '../../common/avatar.js';
 import { io } from 'socket.io-client';
 import draw from './draw.js';
-import { bonusImages } from './utils.js';
+import { bonusImages } from '../../common/utils.js';
 import Render from './render.js';
 import Afficher from './afficher.js';
 import setHtml from './setHtml.js';
@@ -55,24 +55,6 @@ function resampleCanvas() {
 	canvas.height = canvas.clientHeight;
 }
 
-document.addEventListener('keydown', event => {
-	let canShoot = true;
-	avatar.changerClick(event);
-	if (event.key === ' ') {
-		if (canShoot) {
-			avatar.tirer();
-			canShoot = false;
-			setTimeout(function () {
-				canShoot = true;
-			}, 100);
-		}
-	}
-});
-
-document.addEventListener('keyup', event => {
-	avatar.changerClick(event);
-});
-
 let newEnemis = [];
 let newBonus = [];
 
@@ -80,157 +62,112 @@ function render() {
 	context.clearRect(0, 0, canvas.width, canvas.height);
 	renderObject.renderBackground(canvas);
 	gameStarted = affichage.isGameStarted();
+
 	if (gameStarted) {
 		context.font = '40pt New Super Mario Font U';
-		for (let i = 1; i < avatars.length; i++) {
-			if (avatars[i] != undefined) {
-				renderObject.renderScores(i, avatars[i], context);
-				renderObject.renderVies(avatars, context, i);
-			}
-		}
-
 		context.fillStyle = 'blue';
+		context.fillText(`0:${min}:${sec}`, canvas.width / 2, 50);
 
-		context.fillText(0 + ':' + min + ':' + sec, canvas.width / 2, 50);
-
-		for (let avatarId in avatars) {
-			renderObject.renderProjectile(context, avatar, avatars, avatarId);
-			for (let avatarId in avatars) {
-				renderObject.renderProjectile(context, avatar, avatars, avatarId);
-			}
-			socket.on('bonusArray', data => {
-				newBonus = data;
-			});
-			newBonus.forEach(bonus => {
-				let img = new Image();
-				img.src = bonusImages[bonus.choix];
-				img.width = 75;
-				img.height = 75;
-				draw(canvas, context, img, bonus.x, bonus.y);
-			});
-			socket.on('enemis', data => {
-				newEnemis = data;
-			});
-			newEnemis.forEach(enemi => {
-				renderObject.renderEnnemi(canvas, context, enemi.x, enemi.y, enemi);
+		Object.entries(avatars)
+			.filter(([avatarId, avatarData]) => avatarData !== undefined)
+			.forEach(([avatarId, avatarData]) => {
+				renderObject.renderScores(avatarId, avatarData, context);
+				renderObject.renderVies(avatars, context, avatarId);
 			});
-			socket.on('bonusArray', data => {
-				newBonus = data;
-			});
-			newBonus.forEach(bonus => {
-				renderObject.renderBonus(
-					canvas,
-					context,
-					bonusImages[bonus.choix],
-					bonus.x,
-					bonus.y
-				);
-			});
-		}
+
+		renderProjectiles();
+		renderBonuses();
+		renderEnemies();
 	}
 
 	requestAnimationFrame(render);
 }
 
+function renderProjectiles() {
+	for (const avatarId in avatars) {
+		renderObject.renderProjectile(context, avatar, avatars, avatarId);
+	}
+}
+
+function renderBonuses() {
+	socket.on('bonusArray', data => {
+		newBonus = data;
+	});
+
+	newBonus.forEach(bonus => {
+		const img = new Image();
+		img.src = bonusImages[bonus.choix];
+		img.width = 75;
+		img.height = 75;
+		draw(canvas, context, img, bonus.x, bonus.y);
+	});
+}
+
+function renderEnemies() {
+	socket.on('enemis', data => {
+		newEnemis = data;
+	});
+
+	newEnemis.forEach(enemi => {
+		renderObject.renderEnnemi(canvas, context, enemi.x, enemi.y, enemi);
+	});
+}
+
 let avatars = [];
 
-let avatarsScore = {}; // Objet pour stocker les scores associés à chaque avatarId
+let avatarsScore = {};
 
 socket.on('dead', avatarId => {
-	// Récupérer le score de l'avatar avant de le supprimer
 	const score = avatars[avatarId] ? avatars[avatarId].score : 0;
 
-	// Stocker le score associé à l'avatarId dans l'objet avatarsScore
 	avatarsScore[avatarId] = score;
 
-	// Supprimer l'avatar du tableau avatars
 	delete avatars[avatarId];
-
-	// Afficher la fin de partie ou effectuer d'autres actions nécessaires
-	// affichage.afficherFinDePartie();
-	console.log(`Avatar ${avatarId} est mort. Score sauvegardé : ${score}`);
 });
 
-socket.on('disconnectEvent', id => {
-	if (avatar.getNom() == id) {
-		// Si l'avatar est celui du joueur local, afficher la fin de partie
-		affichage.afficherFinDePartie();
-	}
-	// Supprimer l'avatar de avatars
-	delete avatars[id];
-});
-
-// Réception des données des avatars depuis le serveur
 socket.on('avatarsData', avatarData => {
-	avatarData.forEach(data => {
-		// Mise à jour des données de l'avatar dans avatars
-		if (avatars[data.id] != undefined) {
-			avatars[data.id].x = data.x;
-			avatars[data.id].y = data.y;
-			avatars[data.id].projectiles = data.projectiles;
-			avatars[data.id].vies = data.vies;
-			avatars[data.id].score = data.score;
-			avatars[data.id].socketId = data.socketId;
-		} else {
-			// Création d'un nouvel avatar dans avatars si celui-ci n'existe pas encore
-			avatars[data.id] = {
-				x: data.x,
-				y: data.y,
-				projectiles: data.projectiles,
-				vies: data.vies,
-				score: data.score,
-				socketId: data.socketId,
-			};
-		}
-	});
+	for (const data of avatarData) {
+		avatars[data.id] = {
+			x: data.x,
+			y: data.y,
+			projectiles: data.projectiles,
+			vies: data.vies,
+			score: data.score,
+			socketId: data.socketId,
+		};
+	}
 });
 
 const keysPressed = {};
 
-document.addEventListener('keydown', event => {
-	keysPressed[event.keyCode] = true;
+document.addEventListener('keydown', handleKeyEvent);
+document.addEventListener('keyup', handleKeyEvent);
+
+function handleKeyEvent(event) {
+	keysPressed[event.keyCode] = event.type === 'keydown';
+
 	if (event.key === ' ') {
 		socket.emit('shoot', {
 			id: `${socket.id}`,
 			shoot: true,
 		});
 	}
-	socket.emit('clickEvent', {
-		id: `${socket.id}`,
-		key: event.keyCode,
-		pressed: true,
-	});
-	event.preventDefault();
-});
-
-document.addEventListener('keyup', event => {
-	keysPressed[event.keyCode] = false;
 
 	socket.emit('clickEvent', {
 		id: `${socket.id}`,
 		key: event.keyCode,
-		pressed: false,
+		pressed: event.type === 'keydown',
 	});
 
 	event.preventDefault();
-});
+}
 
-let endGame = false;
-socket.on('endGame', () => {
-	console.log(
+socket.once('endGame', () => {
+	affichage.afficherFinDePartie(
+		canvas,
 		avatarsScore[1],
 		avatarsScore[2],
 		avatarsScore[3],
 		avatarsScore[4]
 	);
-	if (!endGame) {
-		affichage.afficherFinDePartie(
-			canvas,
-			avatarsScore[1],
-			avatarsScore[2],
-			avatarsScore[3],
-			avatarsScore[4]
-		);
-	}
-	endGame = true;
 });
diff --git a/client/src/render.js b/client/src/render.js
index 96812c8..e9343b3 100644
--- a/client/src/render.js
+++ b/client/src/render.js
@@ -1,5 +1,5 @@
 import draw from './draw.js';
-import { colors } from './utils.js';
+import { colors } from '../../common/utils.js';
 export default class Render {
 	imageEnemi;
 	imageEnemi2;
diff --git a/client/src/Coordinate.js b/common/Coordinate.js
similarity index 100%
rename from client/src/Coordinate.js
rename to common/Coordinate.js
diff --git a/client/src/avatar.js b/common/avatar.js
similarity index 95%
rename from client/src/avatar.js
rename to common/avatar.js
index 424f844..809a10b 100644
--- a/client/src/avatar.js
+++ b/common/avatar.js
@@ -1,6 +1,6 @@
-import { Projectile } from './Projectile.js';
-import Entite from './entite.js';
-import { Hitbox } from './hitbox.js';
+import { Projectile } from '../server/Projectile.js';
+import Entite from '../server/entite.js';
+import { Hitbox } from '../server/hitbox.js';
 export class Avatar extends Entite {
 	nom;
 	vies;
diff --git a/client/src/avatar.test.js b/common/avatar.test.js
similarity index 100%
rename from client/src/avatar.test.js
rename to common/avatar.test.js
diff --git a/client/src/utils.js b/common/utils.js
similarity index 100%
rename from client/src/utils.js
rename to common/utils.js
diff --git a/client/src/Projectile.js b/server/Projectile.js
similarity index 92%
rename from client/src/Projectile.js
rename to server/Projectile.js
index 67c3ffb..65e4717 100644
--- a/client/src/Projectile.js
+++ b/server/Projectile.js
@@ -1,5 +1,5 @@
 import { Hitbox } from './hitbox.js';
-import draw from './draw.js';
+import draw from '../client/src/draw.js';
 import Entite from './entite.js';
 
 export class Projectile extends Entite {
diff --git a/server/bonus.js b/server/bonus.js
index 93b152b..cd1e7f4 100644
--- a/server/bonus.js
+++ b/server/bonus.js
@@ -1,5 +1,5 @@
-import Entite from '../client/src/entite.js';
-import { Hitbox } from '../client/src/hitbox.js';
+import Entite from './entite.js';
+import { Hitbox } from './hitbox.js';
 export default class Bonus extends Entite {
 	nom;
 	taille;
diff --git a/server/enemis.js b/server/enemis.js
index 45aa7ab..cc61a3f 100644
--- a/server/enemis.js
+++ b/server/enemis.js
@@ -1,5 +1,5 @@
-import Entite from '../client/src/entite.js';
-import { Hitbox } from '../client/src/hitbox.js';
+import Entite from './entite.js';
+import { Hitbox } from './hitbox.js';
 export default class enemi extends Entite {
 	vy;
 	vx;
diff --git a/client/src/entite.js b/server/entite.js
similarity index 100%
rename from client/src/entite.js
rename to server/entite.js
diff --git a/server/gameLogic.js b/server/gameLogic.js
new file mode 100644
index 0000000..13b80cc
--- /dev/null
+++ b/server/gameLogic.js
@@ -0,0 +1,144 @@
+import timer from './timer.js';
+
+export function updateGame(
+	io,
+	enemis,
+	bonusArray,
+	avatars,
+	canvasSize,
+	t,
+	firstAvatar,
+	gameStarted,
+	cptConnexion
+) {
+	io.emit('enemis', enemis);
+	io.emit('bonusArray', bonusArray);
+
+	const areAvatarsActive = avatars.some(avatar => !avatar.spectateur);
+
+	if (firstAvatar && areAvatarsActive) {
+		handleActiveAvatars(io, avatars, updateAvatar, canvasSize, t);
+	} else {
+		endGame(io, t, avatars, gameStarted, cptConnexion);
+	}
+}
+
+export function handleActiveAvatars(io, avatars, updateAvatar, canvasSize, t) {
+	const avatarData = [];
+	avatars.forEach(avatar => {
+		updateAvatar(avatar, avatarData, canvasSize, t);
+	});
+	io.emit('avatarsData', avatarData);
+}
+
+export function updateAvatar(avatar, avatarData, canvasSize, t, enemis) {
+	avatar.canvasSize = canvasSize;
+	if (
+		avatar.getStatut() === 'invincibilite' &&
+		t.getTotalTime() - avatar.getStatutTime() === 15
+	) {
+		avatar.setStatut('null');
+	}
+
+	avatar.projectiles.forEach(projectile => projectile.deplacer());
+
+	if (!avatar.spectateur) {
+		avatarData.push({
+			id: avatar.id,
+			x: avatar.getX(),
+			y: avatar.getY(),
+			projectiles: avatar.projectiles,
+			vies: avatar.getVies(),
+			score: avatar.getScore(),
+			socketId: avatar.nom,
+		});
+	}
+
+	handleAvatarEnemiCollisions(avatar, enemis);
+	handleAvatarBonusCollisions(avatar, t);
+}
+
+export function handleAvatarEnemiCollisions(avatar, enemis) {
+	enemis.forEach(enemi => {
+		if (
+			enemi.hitbox.colision(avatar.hitbox) &&
+			avatar.getStatut() !== 'invincibilite'
+		) {
+			handleAvatarEnemiCollisionEffects(avatar, enemi);
+		}
+
+		if (enemi.getVies() < 0) {
+			avatar.incrementScore(5);
+			enemis.splice(enemis.indexOf(enemi), 1);
+		}
+
+		enemi.deplacer();
+
+		avatar.projectiles.forEach(projectile => {
+			if (projectile.hitbox.colision(enemi.hitbox)) {
+				avatar.projectiles.splice(avatar.projectiles.indexOf(projectile), 1);
+				handleProjectileEnemiCollisionEffects(enemi);
+			}
+		});
+	});
+}
+
+export function handleAvatarEnemiCollisionEffects(avatar, enemi) {
+	avatar.decrementScore(5);
+	enemis.splice(enemis.indexOf(enemi), 1);
+	avatar.perdreVie();
+
+	if (canLostLifeAvatar) {
+		canLostLifeAvatar = false;
+		setTimeout(() => {
+			canLostLifeAvatar = true;
+		}, 100);
+	}
+
+	if (avatar.getVies() === 0) {
+		avatar.setSpectateur();
+		io.emit('dead', avatar.id);
+	}
+}
+
+export function handleProjectileEnemiCollisionEffects(enemi) {
+	enemi.perdreVie();
+
+	if (canLostLifeEnemi) {
+		canLostLifeEnemi = false;
+		setTimeout(() => {
+			canLostLifeEnemi = true;
+		}, 1000 / 60);
+	}
+}
+
+export function handleAvatarBonusCollisions(avatar, t) {
+	bonusArray.forEach(bonus => {
+		if (bonus.hitbox.colision(avatar.hitbox)) {
+			handleBonusCollisionEffects(avatar, bonus, t);
+		}
+
+		if (bonus.estExpire(t.getTotalTime())) {
+			bonusArray.splice(bonusArray.indexOf(bonus), 1);
+		}
+	});
+}
+
+export function handleBonusCollisionEffects(avatar, bonus, t) {
+	if (bonusNoms[bonus.getChoix()] === 'vie') {
+		avatar.gagnerVie();
+	} else if (bonusNoms[bonus.getChoix()] === 'invincibilite') {
+		avatar.setStatut('invincibilite');
+		avatar.setStatutTime(t.getTotalTime());
+	}
+
+	bonusArray.splice(bonusArray.indexOf(bonus), 1);
+}
+
+export function endGame(io, t, avatars, gameStarted, cptConnexion) {
+	gameStarted = false;
+	cptConnexion = 0;
+	t = new timer();
+	avatars.length = 0;
+	io.emit('endGame', true);
+}
diff --git a/client/src/hitbox.js b/server/hitbox.js
similarity index 100%
rename from client/src/hitbox.js
rename to server/hitbox.js
diff --git a/client/src/hitbox.test.js b/server/hitbox.test.js
similarity index 100%
rename from client/src/hitbox.test.js
rename to server/hitbox.test.js
diff --git a/server/index.js b/server/index.js
index 368c662..11a5c5d 100644
--- a/server/index.js
+++ b/server/index.js
@@ -1,37 +1,42 @@
+// server.js
 import express from 'express';
 import http from 'http';
 import addWebpackMiddleware from './middlewares/addWebpackMiddleware.js';
 import { Server as IOServer } from 'socket.io';
-import { Avatar } from '../client/src/avatar.js';
+import { Avatar } from '../common/avatar.js';
 import enemi from './enemis.js';
-import { Coordinate } from '../client/src/Coordinate.js';
+import { Coordinate } from '../common/Coordinate.js';
 import timer from './timer.js';
 import Bonus from './bonus.js';
-import { bonusImages, bonusNoms, bonusTaille } from '../client/src/utils.js';
+import { bonusImages, bonusNoms, bonusTaille } from '../common/utils.js';
 
 const app = express();
+const httpServer = http.createServer(app);
+const io = new IOServer(httpServer);
 
-let canvasSize = new Coordinate(1920, 1261);
-
-let canLostLifeAvatar = true;
-let canLostLifeEnemi = true;
+const canvasSize = new Coordinate(1920, 1261);
 let gameStarted = false;
-
 let t = new timer();
-
-const httpServer = http.createServer(app);
-const fileOptions = { root: process.cwd() };
-addWebpackMiddleware(app);
-
-const io = new IOServer(httpServer);
+let canLostLifeAvatar = true;
+let canLostLifeEnemi = true;
+let firstAvatar = false;
+let cptConnexion = 0;
+let canShoot = true;
+let LVL2start = false;
+let LVL3start = false;
+const avatars = [];
+const enemis = [];
+const bonusArray = [];
 
 app.use(express.static('client/public'));
-
 const port = process.env.PORT == null ? 8000 : process.env.PORT;
+
 httpServer.listen(port, () => {
 	console.log(`Server running at http://localhost:${port}/`);
 });
 
+addWebpackMiddleware(app);
+
 setInterval(function () {
 	if (gameStarted) {
 		t.addTime();
@@ -41,106 +46,87 @@ setInterval(function () {
 	}
 }, 1000);
 
-const avatars = [];
-const enemis = [];
-const bonusArray = [];
-
-let cpt = 0;
-let canShoot = true;
-let LVL2start = false;
-let LVL3start = false;
-
-let firstAvatar = false;
-
 io.on('connection', socket => {
-	cpt++;
-	if (cpt <= 4) {
+	cptConnexion++;
+	if (cptConnexion <= 4) {
 		firstAvatar = true;
-		const avatar = new Avatar(`${socket.id}`, cpt);
-		io.emit('newAvatar', { id: cpt, x: avatar.getX(), y: avatar.getY() });
+		const avatar = new Avatar(`${socket.id}`, cptConnexion);
+		io.emit('newAvatar', {
+			id: cptConnexion,
+			x: avatar.getX(),
+			y: avatar.getY(),
+		});
 		avatars.push(avatar);
 
 		socket.on('disconnect', () => {
-			avatars.forEach(avatar => {
-				if (avatar.nom == socket.id) {
-					io.emit('disconnectEvent', avatar.id);
-					avatars.splice(avatars.indexOf(avatar), 1);
-				}
-			});
-			console.log(`Déconnexion du client ${socket.id}`);
+			const disconnectedAvatarIndex = avatars.findIndex(
+				avatar => avatar.nom === socket.id
+			);
+			if (disconnectedAvatarIndex !== -1) {
+				io.emit('disconnectEvent', avatars[disconnectedAvatarIndex].id);
+				avatars.splice(disconnectedAvatarIndex, 1);
+			}
+			console.log(`Client disconnected: ${socket.id}`);
 		});
 
 		socket.on('start', s => {
-			if (s == true && cpt != 0) {
+			if (s && cptConnexion !== 0) {
 				gameStarted = s;
-			} else if (cpt == 0) {
+			} else if (cptConnexion === 0) {
 				gameStarted = false;
 			}
 		});
 
+		// Handling click events
 		socket.on('clickEvent', clickEvent => {
 			const playerAvatar = avatars.find(avatar => avatar.nom === clickEvent.id);
 			if (playerAvatar) {
 				playerAvatar.click[clickEvent.key] = clickEvent.pressed;
 			} else {
-				console.log(`Aucun avatar trouvé avec le nom ${clickEvent.id}`);
+				console.log(`No avatar found with the name ${clickEvent.id}`);
 			}
 		});
 
 		socket.on('shoot', shoot => {
 			const playerAvatar = avatars.find(avatar => avatar.nom === shoot.id);
-
-			if (canShoot) {
+			if (canShoot && playerAvatar) {
 				playerAvatar.tirer();
 				canShoot = false;
-				setTimeout(function () {
+				setTimeout(() => {
 					canShoot = true;
 				}, 200);
 			}
 		});
 
 		socket.on('canvasSize', canvasSize => {
-			console.log(canvasSize);
 			canvasSize = canvasSize;
 		});
 	}
 });
 
-let spawnIntervalLV1 = setInterval(() => {
-	if (gameStarted) {
-		if (t.getMin() >= 1) {
-			LVL2start = true;
-		}
-		if (t.getSec() >= 30) {
-			LVL3start = true;
-		}
-
-		let randomY = Math.random() * (canvasSize.height - 0) + 0;
-		do {
-			randomY = Math.random() * (canvasSize.height - 0) + 0;
-		} while (randomY > canvasSize.height - 57);
-		const newEnemy = new enemi(canvasSize.width, randomY, 0, 1);
-		enemis.push(newEnemy);
-	}
-}, 1000);
-
-let spawnIntervalLV2 = setInterval(() => {
-	if (LVL2start && gameStarted) {
-		let randomY = Math.random() * (canvasSize.height - 100);
-		const newEnemy = new enemi(canvasSize.width - 100, randomY, 1, 2);
-		enemis.push(newEnemy);
-	}
-}, 800);
+const spawnIntervals = [
+	{ delay: 1000, startCondition: () => gameStarted },
+	{ delay: 800, startCondition: () => LVL2start && gameStarted },
+	{ delay: 4000, startCondition: () => LVL3start && gameStarted },
+];
 
-let spawnIntervalLV3 = setInterval(() => {
-	if (LVL3start && gameStarted) {
-		let randomY = Math.random() * (canvasSize.height - 100);
-		const newEnemy = new enemi(canvasSize.width - 100, randomY, 1, 3);
-		enemis.push(newEnemy);
-	}
-}, 4000);
+spawnIntervals.forEach(({ delay, startCondition }, index) => {
+	setInterval(() => {
+		if (startCondition()) {
+			let randomY =
+				Math.random() * (canvasSize.height - (index === 0 ? 0 : 100));
+			const newEnemy = new enemi(
+				canvasSize.width - (index === 0 ? 0 : 100),
+				randomY,
+				index,
+				index + 1
+			);
+			enemis.push(newEnemy);
+		}
+	}, delay);
+});
 
-let spawnBonusInterval = setInterval(() => {
+setInterval(() => {
 	if (gameStarted) {
 		let randomX;
 		let randomY;
@@ -157,96 +143,140 @@ let spawnBonusInterval = setInterval(() => {
 }, 15000);
 
 setInterval(() => {
+	updateGame();
+}, 1000 / 60);
+
+function updateGame() {
 	io.emit('enemis', enemis);
 	io.emit('bonusArray', bonusArray);
 
-	let areAvatarsActive = avatars.some(avatar => !avatar.spectateur);
+	const areAvatarsActive = avatars.some(avatar => !avatar.spectateur);
 
 	if (firstAvatar && areAvatarsActive) {
-		let avatarData = [];
-		avatars.forEach(avatar => {
-			avatar.canvasSize = canvasSize;
-			if (
-				avatar.getStatut() == 'invincibilite' &&
-				t.getTotalTime() - avatar.getStatutTime() == 15
-			) {
-				avatar.setStatut('null');
-			}
-			enemis.forEach(enemi => {
-				if (
-					enemi.hitbox.colision(avatar.hitbox) &&
-					avatar.getStatut() != 'invincibilite'
-				) {
-					if (canLostLifeAvatar) {
-						avatar.decrementScore(5);
-						enemis.splice(enemis.indexOf(enemi), 1);
-						avatar.perdreVie();
-						canLostLifeAvatar = false;
-						setTimeout(function () {
-							canLostLifeAvatar = true;
-						}, 100);
-					}
-					if (avatar.getVies() == 0) {
-						avatar.setSpectateur();
-						io.emit('dead', avatar.id);
-					}
-				}
-				if (enemi.getVies() < 0) {
-					avatar.incrementScore(5);
-					enemis.splice(enemis.indexOf(enemi), 1);
-				}
-				enemi.deplacer();
-				avatar.colision(enemi.hitbox);
-				avatar.projectiles.forEach(projectile => {
-					if (projectile.hitbox.colision(enemi.hitbox)) {
-						avatar.projectiles.splice(
-							avatar.projectiles.indexOf(projectile),
-							1
-						);
-						if (canLostLifeEnemi) {
-							enemi.perdreVie();
-							canLostLifeEnemi = false;
-							setTimeout(function () {
-								canLostLifeEnemi = true;
-							}, 1000 / 60);
-						}
-					}
-				});
-			});
-			avatar.deplacer();
-			avatar.projectiles.forEach(projectile => projectile.deplacer());
-			if (!avatar.spectateur) {
-				avatarData.push({
-					id: avatar.id,
-					x: avatar.getX(),
-					y: avatar.getY(),
-					projectiles: avatar.projectiles,
-					vies: avatar.getVies(),
-					score: avatar.getScore(),
-					socketId: avatar.nom,
-				});
-			}
-			bonusArray.forEach(bonus => {
-				if (bonus.hitbox.colision(avatar.hitbox)) {
-					if (bonusNoms[bonus.getChoix()] == 'vie') {
-						avatar.gagnerVie();
-					} else if (bonusNoms[bonus.getChoix()] == 'invincibilite') {
-						avatar.setStatut('invincibilite');
-						avatar.setStatutTime(t.getTotalTime());
-					}
-					bonusArray.splice(bonusArray.indexOf(bonus), 1);
-				}
-				if (bonus.estExpire(t.getTotalTime())) {
-					bonusArray.splice(bonusArray.indexOf(bonus), 1);
-				}
-			});
-		});
-		io.emit('avatarsData', avatarData);
+		handleActiveAvatars();
 	} else {
-		gameStarted = false;
-		cpt = 0;
-		t = new timer();
-		avatars.length = 0; // Réinitialiser le tableau d'avatars
-		io.emit('endGame', true);
+		endGame();
 	}
-}, 1000 / 60);
+}
+
+function handleActiveAvatars() {
+	const avatarData = [];
+	avatars.forEach(avatar => {
+		updateAvatar(avatar, avatarData);
+	});
+	io.emit('avatarsData', avatarData);
+}
+
+function updateAvatar(avatar, avatarData) {
+	avatar.canvasSize = canvasSize;
+	if (
+		avatar.getStatut() === 'invincibilite' &&
+		t.getTotalTime() - avatar.getStatutTime() === 15
+	) {
+		avatar.setStatut('null');
+	}
+
+	enemis.forEach(enemi => {
+		handleAvatarEnemiCollisions(avatar, enemi);
+	});
+
+	avatar.deplacer();
+	avatar.projectiles.forEach(projectile => projectile.deplacer());
+
+	if (!avatar.spectateur) {
+		avatarData.push({
+			id: avatar.id,
+			x: avatar.getX(),
+			y: avatar.getY(),
+			projectiles: avatar.projectiles,
+			vies: avatar.getVies(),
+			score: avatar.getScore(),
+			socketId: avatar.nom,
+		});
+	}
+
+	handleAvatarBonusCollisions(avatar);
+}
+
+function handleAvatarEnemiCollisions(avatar, enemi) {
+	if (
+		enemi.hitbox.colision(avatar.hitbox) &&
+		avatar.getStatut() !== 'invincibilite'
+	) {
+		handleAvatarEnemiCollisionEffects(avatar, enemi);
+	}
+
+	if (enemi.getVies() < 0) {
+		avatar.incrementScore(5);
+		enemis.splice(enemis.indexOf(enemi), 1);
+	}
+
+	enemi.deplacer();
+
+	avatar.projectiles.forEach(projectile => {
+		if (projectile.hitbox.colision(enemi.hitbox)) {
+			avatar.projectiles.splice(avatar.projectiles.indexOf(projectile), 1);
+			handleProjectileEnemiCollisionEffects(avatar, enemi);
+		}
+	});
+}
+
+function handleAvatarEnemiCollisionEffects(avatar, enemi) {
+	avatar.decrementScore(5);
+	enemis.splice(enemis.indexOf(enemi), 1);
+	avatar.perdreVie();
+
+	if (canLostLifeAvatar) {
+		canLostLifeAvatar = false;
+		setTimeout(() => {
+			canLostLifeAvatar = true;
+		}, 100);
+	}
+
+	if (avatar.getVies() === 0) {
+		avatar.setSpectateur();
+		io.emit('dead', avatar.id);
+	}
+}
+
+function handleProjectileEnemiCollisionEffects(enemi) {
+	enemi.perdreVie();
+
+	if (canLostLifeEnemi) {
+		canLostLifeEnemi = false;
+		setTimeout(() => {
+			canLostLifeEnemi = true;
+		}, 1000 / 60);
+	}
+}
+
+function handleAvatarBonusCollisions(avatar) {
+	bonusArray.forEach(bonus => {
+		if (bonus.hitbox.colision(avatar.hitbox)) {
+			handleBonusCollisionEffects(avatar, bonus);
+		}
+
+		if (bonus.estExpire(t.getTotalTime())) {
+			bonusArray.splice(bonusArray.indexOf(bonus), 1);
+		}
+	});
+}
+
+function handleBonusCollisionEffects(avatar, bonus) {
+	if (bonusNoms[bonus.getChoix()] === 'vie') {
+		avatar.gagnerVie();
+	} else if (bonusNoms[bonus.getChoix()] === 'invincibilite') {
+		avatar.setStatut('invincibilite');
+		avatar.setStatutTime(t.getTotalTime());
+	}
+
+	bonusArray.splice(bonusArray.indexOf(bonus), 1);
+}
+
+function endGame() {
+	gameStarted = false;
+	cptConnexion = 0;
+	t = new timer();
+	avatars.length = 0;
+	io.emit('endGame', true);
+}
diff --git a/client/src/projectile.test.js b/server/projectile.test.js
similarity index 100%
rename from client/src/projectile.test.js
rename to server/projectile.test.js
-- 
GitLab