Compare commits

..

4 Commits

Author SHA1 Message Date
84cd04a4cf fix: ajout de protection anti-mauvais chargement de voix 2026-08-09 02:29:37 +02:00
dab95812cc Merge branch 'main' into dev
recuperation de la version valide du projet
2026-08-04 10:20:45 +02:00
c4370815f2 fix: correction d'erreur dans le choix de bonne sortie 2026-08-03 00:21:31 +02:00
4d57dfa233 fix: correction d'erreurs de copie 2026-08-02 23:27:55 +02:00
13 changed files with 99 additions and 45 deletions

View File

@@ -9,6 +9,7 @@
</head> </head>
<body> <body>
<h1>Echoes</h1> <h1>Echoes</h1>
<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>
<div id="gameOverScreen" style="display: none"> <div id="gameOverScreen" style="display: none">
@@ -21,6 +22,7 @@
<p>Vous avec franchi les portes a temps.</p> <p>Vous avec franchi les portes a temps.</p>
</div> </div>
<script src="/socket.io/socket.io.js"></script>
<script type="module" src="js/main.js"></script> <script type="module" src="js/main.js"></script>
</body> </body>
</html> </html>

View File

@@ -15,7 +15,7 @@ export function drawMaze() {
ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.clearRect(0, 0, canvas.width, canvas.height);
for (let y = 0; y < maze.length; y++) { for (let y = 0; y < maze.length; y++) {
for (let x = 0; y < maze.length; x++) { for (let x = 0; x < maze[y].length; x++) {
if (maze[y][x] === 1) ctx.fillStyle = "#333"; if (maze[y][x] === 1) ctx.fillStyle = "#333";
else if (maze[y][x] === 2) ctx.fillStyle = "gold"; else if (maze[y][x] === 2) ctx.fillStyle = "gold";
else ctx.fillStyle = "#eee"; else ctx.fillStyle = "#eee";

View File

@@ -1,12 +1,69 @@
/* /*
guideVoice.js guideVoice.js
vocalise les instructions recue du serveur via la synthese vocal du navigateur Vocalise les instructions recues du serveur via la synthese vocale du navigateur
*/ */
export function speakInstruction(texte) { let currentUtterance = null;
speechSynthesis.cancel(); let voixChoisie = null;
const enonce = new SpeechSynthesisUtterance(texte); let voixPretes = false;
enonce.lang = "fr-FR"; let texteEnAttente = null;
speechSynthesis.speak(enonce);
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);
} }

View File

@@ -9,13 +9,14 @@ import { roleState } from "./role.js";
import { render, drawBlind } from "./canvas.js"; import { render, drawBlind } from "./canvas.js";
import { setupMovement } from "./movement.js"; import { setupMovement } from "./movement.js";
import { showGameOver, showVictory, updateTimerDisplay } from "./ui.js"; import { showGameOver, showVictory, updateTimerDisplay } from "./ui.js";
import { speakInstruction} from "guideVoice.js"; import { speakInstruction} from "./guideVoice.js";
setupMovement(); setupMovement();
socket.on("status", (msg) => console.log("Status: ", (msg)); socket.on("status", (msg) => console.log("Status: ", msg));
roleState.current = role;
socket.on("role", (role) => {
roleState.current = role;
if (role === "explorateur") { if (role === "explorateur") {
render(); render();
} else { } else {
@@ -50,5 +51,6 @@ socket.on("timer", (timeLeft) => {
}); });
socket.on("instruction", (data) => { socket.on("instruction", (data) => {
console.log("Payload recu", data);
speakInstruction(data.texte); speakInstruction(data.texte);
}) })

View File

@@ -12,5 +12,5 @@ export const maze = [
[1,0,1,0,1,0,1], [1,0,1,0,1,0,1],
[1,0,1,1,1,0,1], [1,0,1,1,1,0,1],
[1,0,0,0,0,0,1], [1,0,0,0,0,0,1],
[1,1,1,1,1,1,1] [1,1,2,1,1,2,1]
]; ];

View File

@@ -1,19 +0,0 @@
const express = require("express");
const path = require("path");
const http = require("http");
const { Server } = require("socket.io");
const { PORT } = require("./config");
const { registerSocketHandlers } = require("./socketHandlers");
const app = express();
const server = http.createServer(app);
const wSS = new Server(server); // wSS pour webSocketServer
app.use(express.static(path.join(__dirname, "..", "client")));
wSS.on("connection", (socket) => registerSocketHandlers(wSS, socket));
server.listen(PORT, () => {
console.log(`serveur demarre sur http://localhost:${PORT}`);
});

View File

@@ -4,7 +4,7 @@
centralise les constante de reglage du jeu centralise les constante de reglage du jeu
*/ */
module.export = { module.exports = {
PORT: process.env.PORT || 3000, PORT: process.env.PORT || 3000,
GAME_DURATION: 60, GAME_DURATION: 60,
DOORS_TO_WIN: 3, DOORS_TO_WIN: 3,

View File

@@ -11,7 +11,7 @@
const { correctDoor, startPosition } = require("./maze"); const { correctDoor, startPosition } = require("./maze");
const { DOORS_TO_WIN } = require("./config"); const { DOORS_TO_WIN } = require("./config");
fonction handleDoorReached(wSS, socket, position) { function handleDoorReached(wSS, socket, position) {
const pairState = socket.data.pairState; const pairState = socket.data.pairState;
if(!pairState || pairState.gameOver) return; // partie deja terminee if(!pairState || pairState.gameOver) return; // partie deja terminee
@@ -24,12 +24,12 @@ fonction handleDoorReached(wSS, socket, position) {
const result = { correct: false }; const result = { correct: false };
socket.emit("door_result", result); socket.emit("door_result", result);
if (partnerSocket) partnerSocket.emit("door_result", result); if (partnerSocket) partnerSocket.emit("door_result", result);
result; return;
} }
pairState.doorsPassed++; pairState.doorsPassed++;
if (pairState.doorPassed >= DOORS_TO_WIN) { if (pairState.doorsPassed >= DOORS_TO_WIN) {
pairState.gameOver = true; pairState.gameOver = true;
socket.emit("victory"); socket.emit("victory");
if (partnerSocket) partnerSocket.emit("victory"); if (partnerSocket) partnerSocket.emit("victory");

View File

@@ -8,7 +8,7 @@ const INSTRUCTION_GUIDE = "instruction";
const INSTRUCTION_INTERVAL_MS = 5000; const INSTRUCTION_INTERVAL_MS = 5000;
function startInstructionLoop(wSS, socket, partnerSocket, pairState, getInstructionTexte) { function startInstructionLoop(wSS, socket, partnerSocket, pairState, getInstructionText) {
const interval = setInterval(() => { const interval = setInterval(() => {
if (pairState.gameOver) { if (pairState.gameOver) {
clearInterval(interval); clearInterval(interval);

View File

@@ -4,7 +4,7 @@
possede aussi l'information du point de depart possede aussi l'information du point de depart
*/ */
const correctDoor = { x: 3, y: 6}; const correctDoor = { x: 2, y: 6};
const startPosition = { x: 1, y: 1 }; const startPosition = { x: 1, y: 1 };
module.exports = { correctDoor, startPosition }; module.exports = { correctDoor, startPosition };

View File

@@ -9,4 +9,17 @@ const path = require("path");
const http = require("http"); const http = require("http");
const { Server } = require("socket.io"); const { Server } = require("socket.io");
const { PORT } = require("./config.js"); const { PORT } = require("./config");
const { registerSocketHandlers } = require("./socketHandler");
const app = express();
const server = http.createServer(app);
const io = new Server(server);
app.use(express.static(path.join(__dirname, "..", "client")));
io.on("connection", (socket) => registerSocketHandlers(io, socket));
server.listen(PORT, () => {
console.log(`Serveur démarré sur http://localhost:${PORT}`);
});

View File

@@ -8,9 +8,9 @@ const gameState = require("./gameState");
const { GAME_DURATION } = require("./config"); const { GAME_DURATION } = require("./config");
const { handleDoorReached } = require("./doorLogic"); const { handleDoorReached } = require("./doorLogic");
const { startTimer } = require("./timer"); const { startTimer } = require("./timer");
const { startInstruction } = require("./guideInstruction"); const { startInstructionLoop } = require("./guideInstruction");
function registerSocketHandlers(io, socket) { function registerSocketHandlers(wSS, socket) {
console.log(`Un client s'est connecté : ${socket.id}`); console.log(`Un client s'est connecté : ${socket.id}`);
if (gameState.waitingPlayer === null) { if (gameState.waitingPlayer === null) {
@@ -28,7 +28,6 @@ function registerSocketHandlers(io, socket) {
partner.data.role = partnerRole; partner.data.role = partnerRole;
socket.data.partnerId = partner.id; socket.data.partnerId = partner.id;
partner.data.partnerId = socket.id; partner.data.partnerId = socket.id;
// État partagé par référence entre les deux sockets de la paire // État partagé par référence entre les deux sockets de la paire
const pairState = { gameOver: false, doorsPassed: 0, timeLeft: GAME_DURATION }; const pairState = { gameOver: false, doorsPassed: 0, timeLeft: GAME_DURATION };
socket.data.pairState = pairState; socket.data.pairState = pairState;
@@ -40,12 +39,12 @@ function registerSocketHandlers(io, socket) {
partner.emit("status", "Partie prête !"); partner.emit("status", "Partie prête !");
startTimer(socket, partner, pairState); startTimer(socket, partner, pairState);
startInstructionLoop(io, socket , partner, pairState, () => { startInstructionLoop(wSS, socket , partner, pairState, () => {
return "Descendre de quatre cases, aller a droite de une case, descendre de 1 case"; return "Descendre de quatre cases, aller a droite de une case, descendre de 1 case";
}); });
} }
socket.on("door_reached", (position) => handleDoorReached(io, socket, position)); socket.on("door_reached", (position) => handleDoorReached(wSS, socket, position));
socket.on("disconnect", () => { socket.on("disconnect", () => {
console.log(`Client déconnecté : ${socket.id}`); console.log(`Client déconnecté : ${socket.id}`);
@@ -61,7 +60,7 @@ function registerSocketHandlers(io, socket) {
partnerSocket.emit("status", "Ton partenaire s'est deconnecte"); partnerSocket.emit("status", "Ton partenaire s'est deconnecte");
} }
} }
)}; });
} }
module.exports = { registerSocketHandlers }; module.exports = { registerSocketHandlers };

View File

@@ -4,9 +4,9 @@
minuteur autoritaire cote serveur minuteur autoritaire cote serveur
*/ */
const GAME_DURATION = {"./config"}; const { GAME_DURATION } = require("./config");
fonction startTimer(socket, partnerSocket, pairState) { function startTimer(socket, partnerSocket, pairState) {
const interval = setInterval(() => { const interval = setInterval(() => {
if (pairState.gameOver) { if (pairState.gameOver) {
clearInterval(interval); clearInterval(interval);