Compare commits
8 Commits
32b042b72c
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| 84cd04a4cf | |||
| dab95812cc | |||
| ab4372c57c | |||
| d36c025cf2 | |||
| c4370815f2 | |||
| 4d57dfa233 | |||
| 45ad31d0ae | |||
| c709064062 |
@@ -2,146 +2,27 @@
|
|||||||
<html lang="fr">
|
<html lang="fr">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Ma page</title>
|
<title>Echoes</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: sans-serif; max-width: 520px; margin: 40px auto; padding: 0 20px; }
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h1>Bonjour !</h1>
|
<h1>Echoes</h1>
|
||||||
<p>Ceci est une page statique servie par Express.</p>
|
<p id="timerDisplay">Temps restant : --</p>
|
||||||
<canvas id="maze" width="490" height="490" style="border: 1px solid #333;"></canvas>
|
<canvas id="maze" width="490" height="490" style="border: 1px solid #333;"></canvas>
|
||||||
<input type="text" id="input" placeholder="message">
|
|
||||||
<button id="sendBtn">Envoyer</button>
|
|
||||||
<script src="/socket.io/socket.io.js"></script>
|
|
||||||
<script>
|
|
||||||
const socket = io();
|
|
||||||
let myRole = null;
|
|
||||||
console.log("Connecte au serveur, id :", socket.id);
|
|
||||||
|
|
||||||
socket.on("new_round", (startPos) => {
|
<div id="gameOverScreen" style="display: none">
|
||||||
player.x = startPos.x;
|
|
||||||
player.y = startPos.y;
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on("role", (role) => {
|
|
||||||
console.log("Mon role :", role);
|
|
||||||
myRole = role;
|
|
||||||
|
|
||||||
if (role === "explorateur"){
|
|
||||||
canvas.style.display = "block";
|
|
||||||
render();
|
|
||||||
} else {
|
|
||||||
drawBlind();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on("status", (msg) => console.log("Status :", msg));
|
|
||||||
document.getElementById("sendBtn").addEventListener("click", () => {
|
|
||||||
const text = document.getElementById("input").value;
|
|
||||||
socket.emit("message", text);
|
|
||||||
});
|
|
||||||
socket.on("message", (data) => {
|
|
||||||
console.log("message recu du partenaire :", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
const maze = [
|
|
||||||
[1,1,1,1,1,1,1],
|
|
||||||
[1,0,0,0,1,0,1],
|
|
||||||
[1,0,1,0,1,0,1],
|
|
||||||
[1,0,1,0,1,0,1],
|
|
||||||
[1,0,1,1,1,0,1],
|
|
||||||
[1,0,0,0,0,0,1],
|
|
||||||
[1,1,2,1,1,2,1],
|
|
||||||
];
|
|
||||||
|
|
||||||
const canvas = document.getElementById("maze");
|
|
||||||
const ctx = canvas.getContext("2d");
|
|
||||||
const cellSize = 70;
|
|
||||||
const player = { x : 1, y : 1};
|
|
||||||
|
|
||||||
function drawBlind(){
|
|
||||||
ctx.fillStyle = "black";
|
|
||||||
ctx.fillRect(0,0, canvas.width, canvas.height);
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawMaze(){
|
|
||||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
||||||
|
|
||||||
for (let y = 0; y < maze.length; y++){
|
|
||||||
for (let x = 0; x < maze[y].length; x++){
|
|
||||||
if (maze[y][x] === 1)ctx.fillStyle = "#333";
|
|
||||||
else if (maze[y][x] === 2) ctx.fillStyle = "gold";
|
|
||||||
else ctx.fillStyle = "#eee";
|
|
||||||
|
|
||||||
ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function drawPlayer(){
|
|
||||||
ctx.fillStyle = "red";
|
|
||||||
ctx.beginPath();
|
|
||||||
ctx.arc(
|
|
||||||
player.x * cellSize + cellSize / 2,
|
|
||||||
player.y * cellSize + cellSize / 2,
|
|
||||||
cellSize / 3,
|
|
||||||
0, Math.PI * 2
|
|
||||||
);
|
|
||||||
ctx.fill();
|
|
||||||
}
|
|
||||||
|
|
||||||
function render(){
|
|
||||||
drawMaze();
|
|
||||||
drawPlayer();
|
|
||||||
}
|
|
||||||
|
|
||||||
function showGameOver(message){
|
|
||||||
document.getElementById("gameOverMessage").textContent = message;
|
|
||||||
document.getElementById("gameOverScreen").style.display = "block";
|
|
||||||
myRole = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener("keydown", (e) => {
|
|
||||||
if (myRole !== "explorateur") return;
|
|
||||||
|
|
||||||
let newX = player.x;
|
|
||||||
let newY = player.y;
|
|
||||||
|
|
||||||
if (e.key === "ArrowUp") newY--;
|
|
||||||
else if (e.key === "ArrowDown") newY++;
|
|
||||||
else if (e.key === "ArrowLeft") newX--;
|
|
||||||
else if (e.key === "ArrowRight") newX++;
|
|
||||||
else return;
|
|
||||||
|
|
||||||
if (maze[newY][newX] === 0 || maze[newY][newX] === 2){
|
|
||||||
player.x = newX;
|
|
||||||
player.y = newY;
|
|
||||||
render();
|
|
||||||
|
|
||||||
if (maze[newY][newX] === 2){
|
|
||||||
socket.emit("door_reached", { x: newX, y: newY});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on("door_result", (result) => {
|
|
||||||
if (result.correct){
|
|
||||||
console.log("Bonne porte ! Victoire (pour cette manche).");
|
|
||||||
} else {
|
|
||||||
showGameOver("Mauvaise porte. Vous avez perdu.");
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
socket.on("victory", ()=> {
|
|
||||||
document.getElementById("victoryScreen").style.display = "block";
|
|
||||||
myRole = null;
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<div id="gameOverScreen" style="display: none;">
|
|
||||||
<h2>Partie terminee</h2>
|
<h2>Partie terminee</h2>
|
||||||
<p id="gameOverMessage"></p>
|
<p id="gameOverMessage"></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="victoryScreen" style="display: none;">
|
<div id="victoryScreen" style="display: none;">
|
||||||
<h2>Victoire !</h2>
|
<h2>Victoire !</h2>
|
||||||
<p>Vous avez franchi les portes a temps.</p>
|
<p>Vous avec franchi les portes a temps.</p>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
|
||||||
|
<script src="/socket.io/socket.io.js"></script>
|
||||||
|
<script type="module" src="js/main.js"></script>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
48
client/js/canvas.js
Normal file
48
client/js/canvas.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
canvas.js
|
||||||
|
|
||||||
|
toute la logique de rendu visuel
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { maze } from "./maze.js";
|
||||||
|
import { player } from "./player.js";
|
||||||
|
|
||||||
|
const canvas = document.getElementById("maze");
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
const cellSize = 70;
|
||||||
|
|
||||||
|
export function drawMaze() {
|
||||||
|
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
for (let y = 0; y < maze.length; y++) {
|
||||||
|
for (let x = 0; x < maze[y].length; x++) {
|
||||||
|
if (maze[y][x] === 1) ctx.fillStyle = "#333";
|
||||||
|
else if (maze[y][x] === 2) ctx.fillStyle = "gold";
|
||||||
|
else ctx.fillStyle = "#eee";
|
||||||
|
|
||||||
|
ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function drawPlayer() {
|
||||||
|
ctx.fillStyle = "red";
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.arc(
|
||||||
|
player.x * cellSize + cellSize / 2,
|
||||||
|
player.y * cellSize + cellSize / 2,
|
||||||
|
cellSize / 3,
|
||||||
|
0, Math.PI * 2
|
||||||
|
);
|
||||||
|
ctx.fill();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function drawBlind() {
|
||||||
|
ctx.fillStyle = "black";
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function render() {
|
||||||
|
drawMaze();
|
||||||
|
drawPlayer();
|
||||||
|
}
|
||||||
69
client/js/guideVoice.js
Normal file
69
client/js/guideVoice.js
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/*
|
||||||
|
guideVoice.js
|
||||||
|
|
||||||
|
Vocalise les instructions recues du serveur via la synthese vocale du navigateur
|
||||||
|
*/
|
||||||
|
|
||||||
|
let currentUtterance = null;
|
||||||
|
let voixChoisie = null;
|
||||||
|
let voixPretes = false;
|
||||||
|
let texteEnAttente = null;
|
||||||
|
|
||||||
|
const NOM_VOIX_PREFERE = "French (France)+female1";
|
||||||
|
|
||||||
|
function resoudreVoix() {
|
||||||
|
if (voixPretes) return; // deja fait, evite le travail en double
|
||||||
|
|
||||||
|
const voix = speechSynthesis.getVoices();
|
||||||
|
if (voix.length === 0) return; // toujours pas chargees
|
||||||
|
|
||||||
|
voixChoisie =
|
||||||
|
voix.find(v => v.name === NOM_VOIX_PREFERE) ||
|
||||||
|
voix.find(v => v.lang.startsWith("fr")) ||
|
||||||
|
voix[0];
|
||||||
|
|
||||||
|
voixPretes = true;
|
||||||
|
console.log("Voix sélectionnée:", voixChoisie?.name);
|
||||||
|
|
||||||
|
if (texteEnAttente) {
|
||||||
|
const texte = texteEnAttente;
|
||||||
|
texteEnAttente = null;
|
||||||
|
jouer(texte);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Piste 1 : l'event, au cas ou il se declenche sur d'autres navigateurs
|
||||||
|
speechSynthesis.onvoiceschanged = resoudreVoix;
|
||||||
|
|
||||||
|
// Piste 2 : sondage en secours, car onvoiceschanged n'est pas fiable sur Firefox/LibreWolf
|
||||||
|
const intervalleSondage = setInterval(() => {
|
||||||
|
resoudreVoix();
|
||||||
|
if (voixPretes) clearInterval(intervalleSondage);
|
||||||
|
}, 200);
|
||||||
|
|
||||||
|
resoudreVoix(); // au cas ou les voix sont deja dispo au chargement du module
|
||||||
|
|
||||||
|
function jouer(texte) {
|
||||||
|
if (speechSynthesis.speaking) {
|
||||||
|
speechSynthesis.cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
currentUtterance = new SpeechSynthesisUtterance(texte);
|
||||||
|
currentUtterance.voice = voixChoisie;
|
||||||
|
currentUtterance.lang = "fr-FR";
|
||||||
|
currentUtterance.onstart = () => console.log("Lecture démarrée");
|
||||||
|
currentUtterance.onend = () => console.log("Lecture terminée");
|
||||||
|
currentUtterance.onerror = (e) => console.error("Erreur synthèse:", e.error, e);
|
||||||
|
|
||||||
|
speechSynthesis.speak(currentUtterance);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function speakInstruction(texte) {
|
||||||
|
if (!voixPretes) {
|
||||||
|
console.log("Voix pas encore prêtes, mise en attente:", texte);
|
||||||
|
texteEnAttente = texte;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
jouer(texte);
|
||||||
|
}
|
||||||
56
client/js/main.js
Normal file
56
client/js/main.js
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
main.js
|
||||||
|
point d'entree pour les scripts
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { socket } from "./socket.js";
|
||||||
|
import { player } from "./player.js";
|
||||||
|
import { roleState } from "./role.js";
|
||||||
|
import { render, drawBlind } from "./canvas.js";
|
||||||
|
import { setupMovement } from "./movement.js";
|
||||||
|
import { showGameOver, showVictory, updateTimerDisplay } from "./ui.js";
|
||||||
|
import { speakInstruction} from "./guideVoice.js";
|
||||||
|
|
||||||
|
setupMovement();
|
||||||
|
|
||||||
|
socket.on("status", (msg) => console.log("Status: ", msg));
|
||||||
|
|
||||||
|
socket.on("role", (role) => {
|
||||||
|
roleState.current = role;
|
||||||
|
if (role === "explorateur") {
|
||||||
|
render();
|
||||||
|
} else {
|
||||||
|
drawBlind();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("new_round", (startPos) => {
|
||||||
|
player.x = startPos.x;
|
||||||
|
player.y = startPos.y;
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("door_result", (result) => {
|
||||||
|
if (!result.correct){
|
||||||
|
roleState.current = null;
|
||||||
|
showGameOver("Mauvaise porte. Vous avez perdu.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("timeout", () => {
|
||||||
|
roleState.current = null;
|
||||||
|
showGameOver("Temps ecoule. Vous avez perdu.");
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("victory", () => {
|
||||||
|
roleState.current = null;
|
||||||
|
showVictory();
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("timer", (timeLeft) => {
|
||||||
|
updateTimerDisplay(timeLeft);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("instruction", (data) => {
|
||||||
|
console.log("Payload recu", data);
|
||||||
|
speakInstruction(data.texte);
|
||||||
|
})
|
||||||
16
client/js/maze.js
Normal file
16
client/js/maze.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
maze.js
|
||||||
|
1 = mur
|
||||||
|
0 = chemin
|
||||||
|
2 = porte
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const maze = [
|
||||||
|
[1,1,1,1,1,1,1],
|
||||||
|
[1,0,0,0,1,0,1],
|
||||||
|
[1,0,1,0,1,0,1],
|
||||||
|
[1,0,1,0,1,0,1],
|
||||||
|
[1,0,1,1,1,0,1],
|
||||||
|
[1,0,0,0,0,0,1],
|
||||||
|
[1,1,2,1,1,2,1]
|
||||||
|
];
|
||||||
36
client/js/movement.js
Normal file
36
client/js/movement.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
movement.js
|
||||||
|
|
||||||
|
ecoute les inputs clavier et annonce au serveur quand une porte est atteinte
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { maze } from "./maze.js";
|
||||||
|
import { player } from "./player.js";
|
||||||
|
import { render } from "./canvas.js";
|
||||||
|
import { socket } from "./socket.js";
|
||||||
|
import { roleState } from "./role.js";
|
||||||
|
|
||||||
|
export function setupMovement() {
|
||||||
|
document.addEventListener("keydown", (e) => {
|
||||||
|
if (roleState.current !== "explorateur") return;
|
||||||
|
|
||||||
|
let newX = player.x;
|
||||||
|
let newY = player.y;
|
||||||
|
|
||||||
|
if (e.key === "ArrowUp") newY--;
|
||||||
|
else if (e.key === "ArrowDown") newY++;
|
||||||
|
else if (e.key === "ArrowLeft") newX--;
|
||||||
|
else if (e.key === "ArrowRight") newX++;
|
||||||
|
else return;
|
||||||
|
|
||||||
|
if (maze[newY][newX] === 0 || maze[newY][newX] === 2) {
|
||||||
|
player.x = newX;
|
||||||
|
player.y = newY;
|
||||||
|
render();
|
||||||
|
|
||||||
|
if (maze[newY][newX] === 2) {
|
||||||
|
socket.emit("door_reached", { x: newX, y: newY});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
7
client/js/player.js
Normal file
7
client/js/player.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
/*
|
||||||
|
player.js
|
||||||
|
|
||||||
|
contient la position du joueur exporte pour que canvas puisse dessine le joueur et que les posiions soient updated
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const player = { x: 1, y: 1};
|
||||||
7
client/js/role.js
Normal file
7
client/js/role.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
/*
|
||||||
|
role.js
|
||||||
|
|
||||||
|
un objet pour que la propriete current reste modifiable
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const roleState = { current : null };
|
||||||
5
client/js/socket.js
Normal file
5
client/js/socket.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
/*
|
||||||
|
socket.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
export const socket = io();
|
||||||
18
client/js/ui.js
Normal file
18
client/js/ui.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/*
|
||||||
|
ui.js
|
||||||
|
|
||||||
|
toute les manipulation du DOM (ce qu'il y a en dehors du canvas)
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function showGameOver(message) {
|
||||||
|
document.getElementById("gameOverMessage").textContent = message;
|
||||||
|
document.getElementById("gameOverScreen").style.display = "block";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function showVictory() {
|
||||||
|
document.getElementById("victoryScreen").style.display = "block";
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateTimerDisplay(timeLeft) {
|
||||||
|
document.getElementById("timerDisplay").textContent = `Temps restant : ${timeLeft}s`;
|
||||||
|
}
|
||||||
11
server/config.js
Normal file
11
server/config.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
config.js
|
||||||
|
|
||||||
|
centralise les constante de reglage du jeu
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
PORT: process.env.PORT || 3000,
|
||||||
|
GAME_DURATION: 60,
|
||||||
|
DOORS_TO_WIN: 3,
|
||||||
|
};
|
||||||
54
server/doorLogic.js
Normal file
54
server/doorLogic.js
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
/*
|
||||||
|
doorLogic.js
|
||||||
|
|
||||||
|
Sert a regrouper toute la logique autour des portes
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
c'est ici uniquement que la logique de victoire ou defaite est faite
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { correctDoor, startPosition } = require("./maze");
|
||||||
|
const { DOORS_TO_WIN } = require("./config");
|
||||||
|
|
||||||
|
function handleDoorReached(wSS, socket, position) {
|
||||||
|
const pairState = socket.data.pairState;
|
||||||
|
if(!pairState || pairState.gameOver) return; // partie deja terminee
|
||||||
|
|
||||||
|
const partnerId = socket.data.partnerId;
|
||||||
|
const partnerSocket = wSS.sockets.sockets.get(partnerId);
|
||||||
|
const isCorrect = position.x === correctDoor.x && position.y === correctDoor.y;
|
||||||
|
|
||||||
|
if (!isCorrect){
|
||||||
|
pairState.gameOver = true;
|
||||||
|
const result = { correct: false };
|
||||||
|
socket.emit("door_result", result);
|
||||||
|
if (partnerSocket) partnerSocket.emit("door_result", result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pairState.doorsPassed++;
|
||||||
|
|
||||||
|
if (pairState.doorsPassed >= DOORS_TO_WIN) {
|
||||||
|
pairState.gameOver = true;
|
||||||
|
socket.emit("victory");
|
||||||
|
if (partnerSocket) partnerSocket.emit("victory");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const temp = socket.data.role;
|
||||||
|
socket.data.role = partnerSocket.data.role;
|
||||||
|
partnerSocket.data.role = temp;
|
||||||
|
|
||||||
|
socket.emit("door_result", { correct: true});
|
||||||
|
socket.emit("new_round", startPosition);
|
||||||
|
socket.emit("role", socket.data.role);
|
||||||
|
|
||||||
|
if (partnerSocket) {
|
||||||
|
partnerSocket.emit("door_result", { correct: true });
|
||||||
|
partnerSocket.emit("new_round", startPosition);
|
||||||
|
partnerSocket.emit("role", partnerSocket.data.role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { handleDoorReached };
|
||||||
12
server/gameState.js
Normal file
12
server/gameState.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
gameState.js
|
||||||
|
|
||||||
|
Etat global du serveur (pas juste la partie entre deux joueurs)
|
||||||
|
On exporte l'objet plutot que la variable
|
||||||
|
*/
|
||||||
|
|
||||||
|
const gameState = {
|
||||||
|
waitingPlayer: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = gameState;
|
||||||
27
server/guideInstruction.js
Normal file
27
server/guideInstruction.js
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
guideInstruction.js
|
||||||
|
|
||||||
|
sert a envoyer au client guide les instructions
|
||||||
|
*/
|
||||||
|
|
||||||
|
const INSTRUCTION_GUIDE = "instruction";
|
||||||
|
const INSTRUCTION_INTERVAL_MS = 5000;
|
||||||
|
|
||||||
|
|
||||||
|
function startInstructionLoop(wSS, socket, partnerSocket, pairState, getInstructionText) {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
if (pairState.gameOver) {
|
||||||
|
clearInterval(interval);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const guideSocket = socket.data.role === "guide" ? socket : partnerSocket;
|
||||||
|
|
||||||
|
const texte = getInstructionText();
|
||||||
|
guideSocket.emit(INSTRUCTION_GUIDE, { texte });
|
||||||
|
}, INSTRUCTION_INTERVAL_MS);
|
||||||
|
|
||||||
|
return () => clearInterval(interval);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { startInstructionLoop };
|
||||||
12
server/maze.js
Normal file
12
server/maze.js
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
maze.js
|
||||||
|
le serveur sais ou sont les bonnes et mauvaises porte et compare la position envoyee par le joueur et la solution
|
||||||
|
possede aussi l'information du point de depart
|
||||||
|
*/
|
||||||
|
|
||||||
|
const correctDoor = { x: 2, y: 6};
|
||||||
|
const startPosition = { x: 1, y: 1 };
|
||||||
|
|
||||||
|
module.exports = { correctDoor, startPosition };
|
||||||
|
|
||||||
|
module.exports = { correctDoor, startPosition };
|
||||||
@@ -4,7 +4,8 @@
|
|||||||
"description": "",
|
"description": "",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"start": "node server.js"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
|
|||||||
126
server/server.js
126
server/server.js
@@ -1,117 +1,25 @@
|
|||||||
const express = require('express');
|
/*
|
||||||
const path = require('path');
|
server.js
|
||||||
const http = require('http');
|
|
||||||
const { Server } = require('socket.io');
|
|
||||||
|
|
||||||
|
Ce fichier sert principalement de point d'entree au programme serveur et a lancer tous les modules necessaires a son fonctionnement
|
||||||
|
*/
|
||||||
|
|
||||||
|
const express = require("express");
|
||||||
|
const path = require("path");
|
||||||
|
const http = require("http");
|
||||||
|
const { Server } = require("socket.io");
|
||||||
|
|
||||||
|
const { PORT } = require("./config");
|
||||||
|
const { registerSocketHandlers } = require("./socketHandler");
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const server = http.createServer(app);
|
const server = http.createServer(app);
|
||||||
const io = new Server(server);
|
const io = new Server(server);
|
||||||
|
|
||||||
const PORT = process.env.PORT || 3000;
|
|
||||||
|
|
||||||
let waitingPlayer = null;
|
|
||||||
|
|
||||||
// Sert tous les fichiers du dossier "client"
|
|
||||||
app.use(express.static(path.join(__dirname, "..", "client")));
|
app.use(express.static(path.join(__dirname, "..", "client")));
|
||||||
|
|
||||||
const correctDoor = { x : 2, y : 6};
|
io.on("connection", (socket) => registerSocketHandlers(io, socket));
|
||||||
const DOORS_TO_WIN = 3;
|
|
||||||
|
|
||||||
io.on("connection", (socket) => {
|
|
||||||
console.log("Un client s'est connecte : ${socket.id}");
|
|
||||||
|
|
||||||
if (waitingPlayer === null){
|
|
||||||
waitingPlayer = socket;
|
|
||||||
socket.emit("status", "En attente d'un second joueur...");
|
|
||||||
} else {
|
|
||||||
const partner = waitingPlayer;
|
|
||||||
waitingPlayer = null;
|
|
||||||
|
|
||||||
const socketIsGuide = Math.random() < 0.5;
|
|
||||||
const socketRole = socketIsGuide ? "guide" : "explorateur";
|
|
||||||
const partnerRole = socketIsGuide ? "explorateur" : "guide";
|
|
||||||
|
|
||||||
socket.data.partnerId = partner.id;
|
|
||||||
partner.data.partnerId = socket.id;
|
|
||||||
|
|
||||||
socket.emit("status", "Partie prete !");
|
|
||||||
partner.emit("status", "Partie prete !");
|
|
||||||
|
|
||||||
socket.data.role = socketRole;
|
|
||||||
partner.data.role = partnerRole;
|
|
||||||
|
|
||||||
const pairState = { gameOver: false, doorsPassed: 0};
|
|
||||||
socket.data.pairState = pairState;
|
|
||||||
partner.data.pairState = pairState;
|
|
||||||
|
|
||||||
socket.emit("role", socketRole);
|
|
||||||
partner.emit("role", partnerRole);
|
|
||||||
}
|
|
||||||
|
|
||||||
socket.on("message", (data) => {
|
|
||||||
const partnerId = socket.data.partnerId;
|
|
||||||
if (partnerId){
|
|
||||||
io.to(partnerId).emit("message", data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on("door_reached", (position) => {
|
|
||||||
const pairState = socket.data.pairState;
|
|
||||||
if (!pairState || pairState.gameOver) return;
|
|
||||||
|
|
||||||
const partnerId = socket.data.partnerId;
|
|
||||||
const partnerSocket = io.sockets.sockets.get(partnerId);
|
|
||||||
const isCorrect = position.x === correctDoor.x && position.y === correctDoor.y;
|
|
||||||
|
|
||||||
if (!isCorrect){
|
|
||||||
pairState.gameOver = true;
|
|
||||||
const result = { correct: false};
|
|
||||||
socket.emit("door_result", result);
|
|
||||||
if (partnerSocket) partnerSocket.emit("door_result", result);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
pairState.doorsPassed++;
|
|
||||||
|
|
||||||
if (pairState.doorsPassed >= DOORS_TO_WIN){
|
|
||||||
pairState.gameOver = true;
|
|
||||||
socket.emit("victory");
|
|
||||||
if (partnerSocket) partnerSocket.emit("victory");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const temp = socket.data.role;
|
|
||||||
socket.data.role = partnerSocket.data.role;
|
|
||||||
partnerSocket.data.role = temp;
|
|
||||||
|
|
||||||
const startPos = { x: 1, y: 1};
|
|
||||||
|
|
||||||
console.log(pairState);
|
|
||||||
const result = {correct: isCorrect};
|
|
||||||
|
|
||||||
socket.emit("door_result", { correct: true});
|
|
||||||
socket.emit("new_round", startPos);
|
|
||||||
socket.emit("role", socket.data.role);
|
|
||||||
|
|
||||||
if (partnerSocket){
|
|
||||||
partnerSocket.emit("door_result", { correct: true });
|
|
||||||
partnerSocket.emit("new_round", startPos);
|
|
||||||
partnerSocket.emit("role", partnerSocket.data.role);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (partnerId){
|
|
||||||
io.to(partnerId).emit("door_result", result);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on("disconnect", () => {
|
|
||||||
console.log("Client deconnecte : ${socket.id}");
|
|
||||||
if (waitingPlayer === socket){
|
|
||||||
waitingPlayer = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
server.listen(PORT, () => {
|
server.listen(PORT, () => {
|
||||||
console.log(`Serveur démarré sur http://localhost:${PORT}`);
|
console.log(`Serveur démarré sur http://localhost:${PORT}`);
|
||||||
});
|
});
|
||||||
|
|||||||
66
server/socketHandler.js
Normal file
66
server/socketHandler.js
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
socketHandlers.js
|
||||||
|
|
||||||
|
Sert a regrouper tous ce qui passe dans une seule connexion
|
||||||
|
*/
|
||||||
|
|
||||||
|
const gameState = require("./gameState");
|
||||||
|
const { GAME_DURATION } = require("./config");
|
||||||
|
const { handleDoorReached } = require("./doorLogic");
|
||||||
|
const { startTimer } = require("./timer");
|
||||||
|
const { startInstructionLoop } = require("./guideInstruction");
|
||||||
|
|
||||||
|
function registerSocketHandlers(wSS, socket) {
|
||||||
|
console.log(`Un client s'est connecté : ${socket.id}`);
|
||||||
|
|
||||||
|
if (gameState.waitingPlayer === null) {
|
||||||
|
gameState.waitingPlayer = socket;
|
||||||
|
socket.emit("status", "En attente d'un second joueur...");
|
||||||
|
} else {
|
||||||
|
const partner = gameState.waitingPlayer;
|
||||||
|
gameState.waitingPlayer = null;
|
||||||
|
|
||||||
|
const socketIsGuide = Math.random() < 0.5;
|
||||||
|
const socketRole = socketIsGuide ? "guide" : "explorateur";
|
||||||
|
const partnerRole = socketIsGuide ? "explorateur" : "guide";
|
||||||
|
|
||||||
|
socket.data.role = socketRole;
|
||||||
|
partner.data.role = partnerRole;
|
||||||
|
socket.data.partnerId = partner.id;
|
||||||
|
partner.data.partnerId = socket.id;
|
||||||
|
// État partagé par référence entre les deux sockets de la paire
|
||||||
|
const pairState = { gameOver: false, doorsPassed: 0, timeLeft: GAME_DURATION };
|
||||||
|
socket.data.pairState = pairState;
|
||||||
|
partner.data.pairState = pairState;
|
||||||
|
|
||||||
|
socket.emit("role", socketRole);
|
||||||
|
partner.emit("role", partnerRole);
|
||||||
|
socket.emit("status", "Partie prête !");
|
||||||
|
partner.emit("status", "Partie prête !");
|
||||||
|
|
||||||
|
startTimer(socket, partner, pairState);
|
||||||
|
startInstructionLoop(wSS, socket , partner, pairState, () => {
|
||||||
|
return "Descendre de quatre cases, aller a droite de une case, descendre de 1 case";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.on("door_reached", (position) => handleDoorReached(wSS, socket, position));
|
||||||
|
|
||||||
|
socket.on("disconnect", () => {
|
||||||
|
console.log(`Client déconnecté : ${socket.id}`);
|
||||||
|
|
||||||
|
if (gameState.waitingPlayer === socket) {
|
||||||
|
gameState.waitingPlayer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const partnerId = socket.data.partnerId;
|
||||||
|
if (partnerId) {
|
||||||
|
const partnerSocket = wSS.sockets.sockets.get(partnerId);
|
||||||
|
if (partnerSocket){
|
||||||
|
partnerSocket.emit("status", "Ton partenaire s'est deconnecte");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { registerSocketHandlers };
|
||||||
30
server/timer.js
Normal file
30
server/timer.js
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
/*
|
||||||
|
timer.js
|
||||||
|
|
||||||
|
minuteur autoritaire cote serveur
|
||||||
|
*/
|
||||||
|
|
||||||
|
const { GAME_DURATION } = require("./config");
|
||||||
|
|
||||||
|
function startTimer(socket, partnerSocket, pairState) {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
if (pairState.gameOver) {
|
||||||
|
clearInterval(interval);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pairState.timeLeft--;
|
||||||
|
|
||||||
|
socket.emit("timer", pairState.timeLeft);
|
||||||
|
partnerSocket.emit("timer", pairState.timeLeft);
|
||||||
|
|
||||||
|
if (pairState.timeLeft <= 0) {
|
||||||
|
pairState.gameOver = true;
|
||||||
|
socket.emit("timeout");
|
||||||
|
partnerSocket.emit("timeout");
|
||||||
|
clearInterval(interval);
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { startTimer };
|
||||||
Reference in New Issue
Block a user