From 70d9786ef2228effc74d4afd57e323c2d30f7dff Mon Sep 17 00:00:00 2001 From: EisiBaer Date: Tue, 11 Jul 2023 17:22:05 +0200 Subject: [PATCH] Added initial selection of player who chooses a BoardEntry in BE --- .../controllers/PlayerControllerMongoose.js | 43 +++++++++++++++++++ src/server/models/PlayerModel.js | 3 +- src/server/websocket/handler.js | 24 ++++++++++- .../components/blocks/BoardEntryCard.vue | 6 ++- src/webapp/components/pages/Game.vue | 3 ++ src/webapp/components/pages/Lobby.vue | 3 ++ src/webapp/components/views/BoardView.vue | 9 +++- src/webapp/components/views/GameView.vue | 7 ++- src/webapp/stores/GameStore.js | 1 + 9 files changed, 93 insertions(+), 6 deletions(-) diff --git a/src/server/controllers/PlayerControllerMongoose.js b/src/server/controllers/PlayerControllerMongoose.js index 66fd1b7..d039de4 100644 --- a/src/server/controllers/PlayerControllerMongoose.js +++ b/src/server/controllers/PlayerControllerMongoose.js @@ -63,6 +63,26 @@ exports.setAllPlayersAcceptAnswers = ( playerIds, acceptAnswersValue ) => { }); } +/** + * Sets one player as isChoosing and other players as isChoosing false + * @param {String} choosingPlayerId + * @param {Array} notChoosingPlayerIds + * @returns A promise which resolves with the choosing player + */ +exports.setAllPlayersAcceptAnswers = ( choosingPlayerId, notChoosingPlayerIds ) => { + return new Promise((resolve, reject) => { + let promiseSetChoosing = PlayerModel.updateOne( { _id: choosingPlayerId }, { isChoosing: true } ); + let promiseSetNotChoosing = PlayerModel.updateMany( { _id: { $in : notChoosingPlayerIds } }, { isChoosing: false } ); + Promise.all( [ promiseSetChoosing, promiseSetNotChoosing ] ) + .then( ( resArr ) => { + resolve( resArr[0] ); + }) + .catch( ( err ) => { + reject( err ); + }) + }); +} + /** * Checks if a player is allowed to currently answer a question * @param {String} playerId @@ -86,6 +106,29 @@ exports.checkPlayerAcceptAnswers = ( playerId ) => { }); } +/** + * Checks if a player is allowed to choose a BoardEntry + * @param {String} playerId + * @returns A promise which resolves with wheter the player is allowed to choose a BoardEntry or not. Rejects if an error occurs. + */ +exports.checkPlayerCanChoose = ( playerId ) => { + return new Promise((resolve, reject) => { + PlayerModel.findById( playerId ) + .then( ( player ) => { + if( player ){ + resolve( player.isChoosing ); + } else { + let playerNotFoundError = new Error(`No player found with id "${playerId}"`); + playerNotFoundError.name = "NotFoundError"; + reject(playerNotFoundError); + } + }) + .catch( ( err ) => { + reject( err ); + }) + }); +} + /** * Checks if a player is allowed to currently answer a question and sets it to the specified value * @param {String} playerId diff --git a/src/server/models/PlayerModel.js b/src/server/models/PlayerModel.js index 6aaada1..228cf8f 100644 --- a/src/server/models/PlayerModel.js +++ b/src/server/models/PlayerModel.js @@ -5,7 +5,8 @@ const Schema = mongoose.Schema; const PlayerSchema = new Schema({ name: { type: String, required: true, maxLength: 100 }, points: { type: Number }, - acceptAnswers: { type: Boolean, default: false } + acceptAnswers: { type: Boolean, default: false }, + isChoosing: { type: Boolean, default: false }, }); // Export model diff --git a/src/server/websocket/handler.js b/src/server/websocket/handler.js index 5942b42..702bd29 100644 --- a/src/server/websocket/handler.js +++ b/src/server/websocket/handler.js @@ -126,10 +126,18 @@ exports.handleMessage = ( gameSocketList, socket, dataRaw ) => { break; case "startGame": if( socket.locals.isHost ){ + let gameOuter; gameController.findGameByIdAndSetStateAndPopulateBoard( socket.locals.game, "IN_PROGRESS" ) .then( ( game ) => { + gameOuter = game; + let randomPlayerIndex = crypto.randomInt(0, game.players.length ); + let randomPlayer = game.players[randomPlayerIndex]; + let otherPlayers = game.players.splice(randomPlayerIndex, 1); + return playerController.setPlayerIsChoosingAndOthersNotChoosing( randomPlayer, otherPlayers ); + }) + .then( ( choosingPlayer ) => { let message = "Game is starting"; - let sendingData = { gameId: game._id, board: game.board }; + let sendingData = { gameId: gameOuter._id, board: gameOuter.board, choosingPlayer: choosingPlayer._id }; sendAllPlayers( socket, gameSocketList, "gameStarted", message, sendingData); resolve(); }) @@ -150,6 +158,20 @@ exports.handleMessage = ( gameSocketList, socket, dataRaw ) => { reject( new Error("Message not sent by host") ); } break; + case "playerChooseBoardEntry": + playerController.checkPlayerCanChoose() + .then( ( canChoose ) => { + if( canChoose ){ + let message = `BoardEntry ${payload.boardEntryIndex} selected`; + let sendingData = { categoryIndex: payload.categoryIndex, boardEntryIndex: payload.boardEntryIndex }; + sendAllPlayers( socket, gameSocketList, "boardEntrySelected", message, sendingData ); + resolve(); + } else { + resolve(); + } + }) + .catch(); + break; case "selectBoard": if( socket.locals.isHost ){ let message = "Board selected"; diff --git a/src/webapp/components/blocks/BoardEntryCard.vue b/src/webapp/components/blocks/BoardEntryCard.vue index a0c3533..623fedf 100644 --- a/src/webapp/components/blocks/BoardEntryCard.vue +++ b/src/webapp/components/blocks/BoardEntryCard.vue @@ -12,6 +12,10 @@ const props = defineProps({ type: Boolean, default: true, }, + isPlayerChoosing: { + type: Boolean, + default: false, + }, }); const emit = defineEmits(["boardEntryCardClicked", "boardEntryAnsweredClicked", "boardEntryAnsweredRevertClicked" ]); @@ -42,7 +46,7 @@ function boardEntryAnsweredRevertClicked(){ { 'border-pink-accent-primary-disabled' : props.boardEntry.isAnswered }, { 'border-pink-accent-primary' : !props.boardEntry.isAnswered }, { 'bg-dark-primary': props.boardEntry.isAnswered }, - { 'pointer': !props.boardEntry.isAnswered && props.isHost }, + { 'pointer': !props.boardEntry.isAnswered && ( props.isHost || props.isPlayerChoosing ) }, ]" @click="questionCardClicked" > diff --git a/src/webapp/components/pages/Game.vue b/src/webapp/components/pages/Game.vue index fff41dd..d2a590b 100644 --- a/src/webapp/components/pages/Game.vue +++ b/src/webapp/components/pages/Game.vue @@ -226,6 +226,8 @@ function showBoard(){ function boardEntryClicked( cIndex, entryIndex ){ if( gameStore.isHost ){ gameStore.sendEvent( "selectBoardEntry", { categoryIndex: cIndex, boardEntryIndex: entryIndex } ); + } else if( gameStore.isPlayerChoosing ){ + gameStore.sendEvent( "playerChooseBoardEntry", { categoryIndex: cIndex, boardEntryIndex: entryIndex } ); } } @@ -390,6 +392,7 @@ onBeforeRouteLeave((to, from) => { :bEIndex="boardEntryIndex" :showingBottomView="true" :isHost="gameStore.isHost" + :isPlayerChoosing="gameStore.isPlayerChoosing" :anyPlayerIsAnswering="anyPlayerIsAnswering" :isBeingPlayed="isBeingPlayed" @showBoard="showBoard" diff --git a/src/webapp/components/pages/Lobby.vue b/src/webapp/components/pages/Lobby.vue index fdd0ee7..895c71c 100644 --- a/src/webapp/components/pages/Lobby.vue +++ b/src/webapp/components/pages/Lobby.vue @@ -29,6 +29,9 @@ function boardSelected( id ){ onMounted( () => { gameStore.addSocketListener("gameStarted", ( data ) => { + if( !gameStore.isHost ){ + gameStore.isPlayerChoosing = data.payload.choosingPlayer === gameStore.playerId; + } router.push( { name: "gameWithGameId", params: { gameId: route.params.gameId } } ); }); diff --git a/src/webapp/components/views/BoardView.vue b/src/webapp/components/views/BoardView.vue index 11f256e..408d2dd 100644 --- a/src/webapp/components/views/BoardView.vue +++ b/src/webapp/components/views/BoardView.vue @@ -12,12 +12,16 @@ const props = defineProps({ type: Boolean, default: true, }, + isPlayerChoosing: { + type: Boolean, + default: false, + }, }); const emit = defineEmits(["boardEntryClicked", "questionAnswered", "questionAnsweredRevert" ]) function boardEntryCardClicked( categoryIndex, boardEntryIndex ){ - if( props.isHost ){ + if( props.isHost || props.isPlayerChoosing ){ emit("boardEntryClicked", categoryIndex, boardEntryIndex); } } @@ -41,7 +45,8 @@ function boardEntryCardClicked( categoryIndex, boardEntryIndex ){ />