Compare commits
6 Commits
45ad31d0ae
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 84cd04a4cf | |||
| dab95812cc | |||
| ab4372c57c | |||
| d36c025cf2 | |||
| c4370815f2 | |||
| 4d57dfa233 |
@@ -9,6 +9,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<h1>Echoes</h1>
|
||||
<p id="timerDisplay">Temps restant : --</p>
|
||||
<canvas id="maze" width="490" height="490" style="border: 1px solid #333;"></canvas>
|
||||
|
||||
<div id="gameOverScreen" style="display: none">
|
||||
@@ -21,6 +22,7 @@
|
||||
<p>Vous avec franchi les portes a temps.</p>
|
||||
</div>
|
||||
|
||||
<script src="/socket.io/socket.io.js"></script>
|
||||
<script type="module" src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -15,7 +15,7 @@ export function drawMaze() {
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
|
||||
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";
|
||||
else if (maze[y][x] === 2) ctx.fillStyle = "gold";
|
||||
else ctx.fillStyle = "#eee";
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -9,12 +9,14 @@ 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));
|
||||
roleState.current = role;
|
||||
socket.on("status", (msg) => console.log("Status: ", msg));
|
||||
|
||||
socket.on("role", (role) => {
|
||||
roleState.current = role;
|
||||
if (role === "explorateur") {
|
||||
render();
|
||||
} else {
|
||||
@@ -47,3 +49,8 @@ socket.on("victory", () => {
|
||||
socket.on("timer", (timeLeft) => {
|
||||
updateTimerDisplay(timeLeft);
|
||||
});
|
||||
|
||||
socket.on("instruction", (data) => {
|
||||
console.log("Payload recu", data);
|
||||
speakInstruction(data.texte);
|
||||
})
|
||||
|
||||
@@ -12,5 +12,5 @@ export const maze = [
|
||||
[1,0,1,0,1,0,1],
|
||||
[1,0,1,1,1,0,1],
|
||||
[1,0,0,0,0,0,1],
|
||||
[1,1,1,1,1,1,1]
|
||||
[1,1,2,1,1,2,1]
|
||||
];
|
||||
19
server.js
19
server.js
@@ -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}`);
|
||||
});
|
||||
@@ -4,7 +4,7 @@
|
||||
centralise les constante de reglage du jeu
|
||||
*/
|
||||
|
||||
module.export = {
|
||||
module.exports = {
|
||||
PORT: process.env.PORT || 3000,
|
||||
GAME_DURATION: 60,
|
||||
DOORS_TO_WIN: 3,
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
const { correctDoor, startPosition } = require("./maze");
|
||||
const { DOORS_TO_WIN } = require("./config");
|
||||
|
||||
fonction handleDoorReached(wSS, socket, position) {
|
||||
function handleDoorReached(wSS, socket, position) {
|
||||
const pairState = socket.data.pairState;
|
||||
if(!pairState || pairState.gameOver) return; // partie deja terminee
|
||||
|
||||
@@ -24,12 +24,12 @@ fonction handleDoorReached(wSS, socket, position) {
|
||||
const result = { correct: false };
|
||||
socket.emit("door_result", result);
|
||||
if (partnerSocket) partnerSocket.emit("door_result", result);
|
||||
result;
|
||||
return;
|
||||
}
|
||||
|
||||
pairState.doorsPassed++;
|
||||
|
||||
if (pairState.doorPassed >= DOORS_TO_WIN) {
|
||||
if (pairState.doorsPassed >= DOORS_TO_WIN) {
|
||||
pairState.gameOver = true;
|
||||
socket.emit("victory");
|
||||
if (partnerSocket) partnerSocket.emit("victory");
|
||||
|
||||
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 };
|
||||
@@ -4,7 +4,7 @@
|
||||
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 };
|
||||
|
||||
module.exports = { correctDoor, startPosition };
|
||||
|
||||
@@ -9,4 +9,17 @@ const path = require("path");
|
||||
const http = require("http");
|
||||
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}`);
|
||||
});
|
||||
|
||||
@@ -8,8 +8,9 @@ const gameState = require("./gameState");
|
||||
const { GAME_DURATION } = require("./config");
|
||||
const { handleDoorReached } = require("./doorLogic");
|
||||
const { startTimer } = require("./timer");
|
||||
const { startInstructionLoop } = require("./guideInstruction");
|
||||
|
||||
function registerSocketHandlers(io, socket) {
|
||||
function registerSocketHandlers(wSS, socket) {
|
||||
console.log(`Un client s'est connecté : ${socket.id}`);
|
||||
|
||||
if (gameState.waitingPlayer === null) {
|
||||
@@ -27,7 +28,6 @@ function registerSocketHandlers(io, socket) {
|
||||
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;
|
||||
@@ -39,9 +39,12 @@ function registerSocketHandlers(io, socket) {
|
||||
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(io, socket, position));
|
||||
socket.on("door_reached", (position) => handleDoorReached(wSS, socket, position));
|
||||
|
||||
socket.on("disconnect", () => {
|
||||
console.log(`Client déconnecté : ${socket.id}`);
|
||||
@@ -57,7 +60,7 @@ function registerSocketHandlers(io, socket) {
|
||||
partnerSocket.emit("status", "Ton partenaire s'est deconnecte");
|
||||
}
|
||||
}
|
||||
)};
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { registerSocketHandlers };
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
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(() => {
|
||||
if (pairState.gameOver) {
|
||||
clearInterval(interval);
|
||||
|
||||
Reference in New Issue
Block a user