diff --git a/multiplayerservice/server.js b/multiplayerservice/server.js index a7c54d7..b3a6f56 100644 --- a/multiplayerservice/server.js +++ b/multiplayerservice/server.js @@ -27,15 +27,6 @@ function createParty() { return partyCode; } -// Join a party -function joinParty(partyCode, socket) { - if (parties[partyCode]) { - parties[partyCode].push(socket); - return true; - } - return false; -} - const updateLobbyUsers = (lobbyCode) => { io.to(lobbyCode).emit('lobbyUsers', Object.values(lobby[lobbyCode])); }; @@ -44,6 +35,30 @@ const broadcastQuestions = (partyCode, questions) => { io.to(partyCode).emit('questionsUpdated', questions); }; +const playerFinished = (partyCode, socketId, totalPoints) => { + console.log(totalPoints) + lobby[partyCode][socketId].finished = true; + lobby[partyCode][socketId].totalPoints = totalPoints; + + const allFinished = Object.values(lobby[partyCode]).every(player => player.finished); + + if (allFinished) { + const playersWithPoints = Object.entries(lobby[partyCode]).map(([id, player]) => { + console.log(player) + return { + finished: player.finished, + username: player.username, + points: player.totalPoints + }}); + + // Sort the players by their total points in descending order + playersWithPoints.sort((a, b) => b.totalPoints - a.totalPoints); + + // Emit an event to notify all clients with the ordered list of players and their points + io.to(partyCode).emit('allPlayersFinished', playersWithPoints); + } +}; + io.on('connection', socket => { console.log('Client connected'); @@ -79,6 +94,20 @@ io.on('connection', socket => { broadcastQuestions(partyCode, questions); }); + socket.on('exitParty', (partyCode) => { + console.log("aqui") + if (lobby[partyCode]) { + // Remove the user from the lobby + delete lobby[partyCode][socket.id]; + // Broadcast updated list of users to all remaining users in the lobby + updateLobbyUsers(partyCode); + } + }); + + socket.on('playerFinished', (partyCode, totalPoints) => { + playerFinished(partyCode, socket.id, totalPoints); + }); + socket.on('disconnect', () => { console.log('Client disconnected'); // Remove the user from the lobby when they disconnect diff --git a/webapp/src/components/game/LobbyGame.scss b/webapp/src/components/game/LobbyGame.scss index adbc23e..4f607f1 100644 --- a/webapp/src/components/game/LobbyGame.scss +++ b/webapp/src/components/game/LobbyGame.scss @@ -76,6 +76,7 @@ table { .button-container { display: flex; justify-content: space-between; + margin-top: 20px; } .start-game-button { @@ -88,6 +89,16 @@ table { margin-left: 10px; } + .exit-lobby-button { + background-color: red; + color: white; + padding: 10px 20px; + border: none; + border-radius: 5px; + cursor: pointer; + margin-left: 10px; + } + .start-game-button:disabled { background-color: #cccccc; /* Change background color for disabled state */ color: #666666; /* Change text color for disabled state */ diff --git a/webapp/src/components/game/LobbyGameMultiPlayer.tsx b/webapp/src/components/game/LobbyGameMultiPlayer.tsx deleted file mode 100644 index e69de29..0000000 diff --git a/webapp/src/components/game/ScoreboardGame.tsx b/webapp/src/components/game/ScoreboardGame.tsx index d0aec5d..28eb8f9 100644 --- a/webapp/src/components/game/ScoreboardGame.tsx +++ b/webapp/src/components/game/ScoreboardGame.tsx @@ -1,14 +1,21 @@ import { FC} from 'react' import { Player } from './singleplayer/GameSinglePlayer'; import './ScoreboardGame.css'; +import { PlayerWithPoints } from './multiplayer/GameMultiPlayer'; interface ScoreboardGameProps { - userScores: Player[]; + userScoresSinglePlayer?: Player[]; + userScoresMultiPlayer?: PlayerWithPoints[]; } -const ScoreboardGame:FC = ({userScores}) => { - const sorted = userScores.sort((a, b) => b.points - a.points); +const ScoreboardGame:FC = ({userScoresSinglePlayer, userScoresMultiPlayer}) => { + let sorted; + if(userScoresSinglePlayer){ + sorted = userScoresSinglePlayer.sort((a, b) => b.points - a.points); + } else if (userScoresMultiPlayer){ + sorted = userScoresMultiPlayer.sort((a, b) => b.points - a.points); + } return (
diff --git a/webapp/src/components/game/multiplayer/GameMultiPlayer.tsx b/webapp/src/components/game/multiplayer/GameMultiPlayer.tsx index 365ef6c..7644e54 100644 --- a/webapp/src/components/game/multiplayer/GameMultiPlayer.tsx +++ b/webapp/src/components/game/multiplayer/GameMultiPlayer.tsx @@ -5,6 +5,7 @@ import { Container } from '@mui/material'; import LobbyMultiPlayer from './LobbyMultiPlayer'; import { Question4Answers } from '../singleplayer/GameSinglePlayer'; import QuestionsMultiPlayer from './QuestionsMultiPlayer'; +import ScoreboardGame from '../ScoreboardGame'; interface GameMultiPlayerProps { @@ -22,7 +23,12 @@ export interface UserPlayer { isAdmin: boolean; } -const GameMultiPlayer: FC = ({}) => { +export interface PlayerWithPoints { + username: string; + points: number; +} + +const GameMultiPlayer: FC = () => { const SERVER_URL = 'http://localhost:8006'; const username = localStorage.getItem("username") @@ -32,6 +38,7 @@ const GameMultiPlayer: FC = ({}) => { const [partyCode, setPartyCode] = useState(""); const [users, setUsers] = useState([]); const [questions, setQuestions] = useState([]); + const [sortedUsersByPoints, setSortedUsersByPoints] = useState([]) useEffect(() => { const newSocket = io(SERVER_URL); @@ -43,24 +50,23 @@ const GameMultiPlayer: FC = ({}) => { }); newSocket.on('joinedParty', (user: UserPlayer) => { - console.log(user) - console.log(`User ${username} joined the party`); setStage(2); - console.log(users) }) newSocket.on('lobbyUsers', (users: UserPlayer[]) => { setUsers(users); - console.log(users) }); newSocket.on('partyNotFound', () => { console.log('Party not found'); }); + newSocket.on('allPlayersFinished', (playersWithPoints:PlayerWithPoints[]) => { + setSortedUsersByPoints(playersWithPoints); + setStage(4); + }) + newSocket.on('questionsUpdated', (questions: Question4Answers[]) => { - console.log('questions recieved from server') - console.log(questions); setQuestions(questions); setStage(3); }) @@ -82,7 +88,8 @@ const GameMultiPlayer: FC = ({}) => { {stage === 1 && } {stage === 2 && } - {stage === 3 && } + {stage === 3 && } + {stage === 4 && } ) } diff --git a/webapp/src/components/game/multiplayer/LobbyMultiPlayer.tsx b/webapp/src/components/game/multiplayer/LobbyMultiPlayer.tsx index 3ac1a33..d21bd1a 100644 --- a/webapp/src/components/game/multiplayer/LobbyMultiPlayer.tsx +++ b/webapp/src/components/game/multiplayer/LobbyMultiPlayer.tsx @@ -1,4 +1,4 @@ -import { FC, useEffect, useState } from 'react' +import { FC, useState } from 'react' import { SocketProps, UserPlayer } from './GameMultiPlayer'; import '../LobbyGame.scss'; import axios from 'axios'; @@ -14,21 +14,18 @@ const LobbyMultiPlayer: FC = ({socket, handleCurrentStage const [isFetched, setFetched] = useState(true); + const uuid = localStorage.getItem("uuid"); + const fetchQuestions = async () => { setFetched(false) const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000'; try { - console.log(users) const requestData = { players: users.map((user) => ({uuid: user.uuid})) } - console.log("requestData") - console.log(requestData) const response = await axios.post(`${apiEndpoint}/createGame`, requestData); - console.log("Juego creado") - console.log(response.data) socket.emit('updateQuestions', partyCode, response.data); setFetched(true); } catch (error) { @@ -36,6 +33,15 @@ const LobbyMultiPlayer: FC = ({socket, handleCurrentStage } }; + const exitLobby = () => { + socket.emit('exitParty', partyCode) + handleCurrentStage(1) + } + + const isAdmin = () => { + return users.some((user) => user.uuid === uuid && user.isAdmin); + } + return (

Lobby - Multiplayer

@@ -50,14 +56,9 @@ const LobbyMultiPlayer: FC = ({socket, handleCurrentStage
))}
- {/* {isFetched && } - {!isFetched && } */} - - {isFetched && } + + {isFetched && isAdmin() && } + {isFetched && !isAdmin() && } {!isFetched && }
diff --git a/webapp/src/components/game/multiplayer/MenuMultiplayer.tsx b/webapp/src/components/game/multiplayer/MenuMultiplayer.tsx index c22dbcb..9512f84 100644 --- a/webapp/src/components/game/multiplayer/MenuMultiplayer.tsx +++ b/webapp/src/components/game/multiplayer/MenuMultiplayer.tsx @@ -27,9 +27,6 @@ const MenuMultiplayer: FC = ({socket, handleCurrentStage, }; const joinParty = () => { - console.log("Joining party...") - console.log(typedCode) - console.log(username) const user: UserPlayer = { username: username, totalPoints: parseInt(totalPoints), diff --git a/webapp/src/components/game/multiplayer/QuestionsMultiPlayer.tsx b/webapp/src/components/game/multiplayer/QuestionsMultiPlayer.tsx index dbde747..90a44b0 100644 --- a/webapp/src/components/game/multiplayer/QuestionsMultiPlayer.tsx +++ b/webapp/src/components/game/multiplayer/QuestionsMultiPlayer.tsx @@ -1,15 +1,94 @@ -import { FC } from 'react' +import { FC, useState } from 'react' import { SocketProps } from './GameMultiPlayer'; import { Question4Answers } from '../singleplayer/GameSinglePlayer'; +import axios from 'axios'; interface QuestionsMultiPlayerProps { socket: SocketProps; handleCurrentStage: (n: number) => void questions: Question4Answers[] + partyCode: string } -const QuestionsMultiPlayer: FC = ({socket,handleCurrentStage,questions}) => { - return
QuestionsMultiPlayer
+const QuestionsMultiPlayer: FC = ({socket, questions, partyCode}) => { + const uuid = localStorage.getItem("userUUID"); + const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000'; + + const [currentQuestion, setCurrentQuestion] = useState(0); + const [correctAnswers, setCorrectAnswers] = useState(0); + + const handleAnswerClick = async (answer: string) => { + if(questions[currentQuestion].correctAnswer === answer){ + setCorrectAnswers(correctAnswers + 1); + + } + setCurrentQuestion(currentQuestion + 1); + if(currentQuestion+2 === questions.length){ + const totalPoints = calculatePoints(correctAnswers, questions.length); + // the player has finished the game + // update stats for each player + const requestData ={ "players": [{ + "uuid": uuid, + "nCorrectAnswers": correctAnswers, + "nWrongAnswers": questions.length - correctAnswers, + "totalScore": totalPoints, + "isWinner": false + }]} + await axios.post(`${apiEndpoint}/updateStats`, requestData); + // pass the points obtained of each player to the socket + socket.emit('playerFinished', partyCode, totalPoints) + } + }; + + const calculatePoints = (correctAnswers: number, totalQuestions: number) => { + const incorrectQuestions = totalQuestions - correctAnswers; + const points = correctAnswers * 100 - incorrectQuestions * 25; + return points; + } + + const getShuffledAnswers = () => { + const answers = [ + questions[currentQuestion].correctAnswer, + questions[currentQuestion].incorrectAnswer1, + questions[currentQuestion].incorrectAnswer2, + questions[currentQuestion].incorrectAnswer3, + ]; + + for (let i = answers.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [answers[i], answers[j]] = [answers[j], answers[i]]; + } + + return answers; + }; + + return ( +
+ {(currentQuestion+1) < questions.length && ( + <> +

Question {currentQuestion + 1}

+

{questions[currentQuestion].question}

+
+ {getShuffledAnswers().map((answer) => ( + + ))} +
+ + )} + {currentQuestion+1 === questions.length && ( + <> +

You answered {correctAnswers} out of {questions.length} questions correctly.

+

You earned {calculatePoints(correctAnswers, questions.length)} points.

+

Waiting for the rest of the players to finish...

+ + )} +
+ ) } export default QuestionsMultiPlayer \ No newline at end of file diff --git a/webapp/src/components/game/singleplayer/GameSinglePlayer.tsx b/webapp/src/components/game/singleplayer/GameSinglePlayer.tsx index 82da1ce..6d064cc 100644 --- a/webapp/src/components/game/singleplayer/GameSinglePlayer.tsx +++ b/webapp/src/components/game/singleplayer/GameSinglePlayer.tsx @@ -79,7 +79,7 @@ const GameSinglePlayer = () => { {currentStage === 1 && ()} {currentStage === 2 && ()} - {currentStage === 3 && ( )} + {currentStage === 3 && ( )} ); };