diff --git a/client/src/Table.css b/client/src/Table.css index 9e3c5ca..8659b1c 100755 --- a/client/src/Table.css +++ b/client/src/Table.css @@ -8,6 +8,26 @@ background-image: url("./assets/tabletop.png"); } + +.WaitingForPlayer { + display: flex; + position: absolute; + left: 0; + right: 40vw; + bottom: 0; + top: 0; + justify-content: center; + align-items: center; + background: rgba(0,0,0,0.5); + z-index: 1000; +} + +.WaitingForPlayer .Title { + align-self: center; + padding: 0.5em; + font-weight: bold; +} + .GameOrder { display: flex; position: absolute; diff --git a/client/src/Table.js b/client/src/Table.js index 692a24e..01cb15e 100755 --- a/client/src/Table.js +++ b/client/src/Table.js @@ -228,10 +228,21 @@ const Chat = ({ table }) => { /* If the date is in the future, set it to now */ const name = item.from ? item.from : item.color, from = name ? `${name}, ` : ''; + let message; + const dice = item.message.match(/^(.*rolled )([1-6])(, ([1-6]))?(.*)$/); + if (dice) { + if (dice[4]) { + message = <>{dice[1]}, {dice[5]}; + } else { + message = <>{dice[1]}{dice[5]}; + } + } else { + message = item.message; + } return ( - {from} Date.now() ? Date.now() : item.date} interval={1000}/>)} /> @@ -267,6 +278,16 @@ const StartButton = ({ table }) => { ); }; +const WaitingForPlayer = ({table}) => { + return ( +
+ { table.game && +
Waiting for {table.game.turn} to complete their turn.
+
} +
+ ); +} + const GameOrder = ({table}) => { const rollClick = (event) => { @@ -334,6 +355,7 @@ const Action = ({ table }) => { } const passClick = (event) => { + return table.passTurn(); } const quitClick = (event) => { @@ -354,7 +376,7 @@ const Action = ({ table }) => { } { table.game.state === 'active' && <> - + } { !inLobby && @@ -479,6 +501,7 @@ class Table extends React.Component { this.rollDice = this.rollDice.bind(this); this.setGameState = this.setGameState.bind(this); this.shuffleTable = this.shuffleTable.bind(this); + this.passTurn = this.passTurn.bind(this); this.updateGame = this.updateGame.bind(this); this.setPlayerName = this.setPlayerName.bind(this); this.setSelected = this.setSelected.bind(this); @@ -638,6 +661,35 @@ class Table extends React.Component { }); } + passTurn() { + if (this.loadTimer) { + window.clearTimeout(this.loadTimer); + this.loadTimer = null; + } + + return window.fetch(`${base}/api/v1/games/${this.state.game.id}/pass`, { + method: "PUT", + cache: 'no-cache', + credentials: 'same-origin', + headers: { + 'Content-Type': 'application/json' + } + }).then((res) => { + if (res.status >= 400) { + throw new Error(`Unable to pass!`); + } + return res.json(); + }).then((game) => { + this.updateGame(game); + this.updateMessage(); + }).catch((error) => { + console.error(error); + this.setState({error: error.message}); + }).then(() => { + this.resetGameLoad(); + }); + } + rollDice() { if (this.loadTimer) { window.clearTimeout(this.loadTimer); @@ -1017,6 +1069,10 @@ class Table extends React.Component { } + { game && game.turn && game.turn !== game.name && + + } + { game && game.showCards &&
{ game && game.state === "active" && <> diff --git a/server/routes/games.js b/server/routes/games.js index e9116f4..ccf6b48 100755 --- a/server/routes/games.js +++ b/server/routes/games.js @@ -154,6 +154,15 @@ const getPlayerName = (game, player) => { return ''; }; +const getPlayerColor = (game, player) => { + for (let color in game.players) { + if (game.players[color] === player) { + return color; + } + } + return ''; +} + const processGameOrder = (game, player, dice) => { let message; @@ -182,8 +191,12 @@ const processGameOrder = (game, player, dice) => { return `${index+1}. ${getPlayerName(game, player)}`; }).join(', ')}.`; addChatMessage(game, null, message); + game.playerOrder = players.map(player => getPlayerColor(game, player)); game.state = 'active' message = `Game has started!`; + game.turn = getPlayerName(game, players[0]); + addChatMessage(game, null, message); + message = `It is ${game.turn}'s turn.`; } else { message = `There are still ties for player order!`; } @@ -216,12 +229,15 @@ const roll = (game, session) => { } game.dice = [ Math.ceil(Math.random() * 6) ]; + message = `${name} rolled ${game.dice[0]}.`; + addChatMessage(game, session, message); + message = undefined; processGameOrder(game, player, game.dice[0]); break; case "active": game.dice = [ Math.ceil(Math.random() * 6), Math.ceil(Math.random() * 6) ]; - message = `${name} rolled ${game.dice[0] + game.dice[1]}.`; + message = `${name} rolled ${game.dice[0]}, ${game.dice[1]}.`; break; default: @@ -346,6 +362,7 @@ const adminActions = (game, action, value) => { delete game.players[key].orderRoll; delete game.players[key].orderStatus; } + delete game.turn; game.state = 'game-order'; break; } @@ -514,6 +531,27 @@ const addChatMessage = (game, session, message) => { }); }; +const getNextPlayer = (game, name) => { + let color; + for (let id in game.sessions) { + if (game.sessions[id].name === name) { + color = game.sessions[id].color; + break; + } + } + if (!color) { + return name; + } + let index = game.playerOrder.indexOf(color); + index = (index + 1) % game.playerOrder.length; + for (let id in game.sessions) { + if (game.sessions[id].color === game.playerOrder[index]) { + return game.sessions[id].name; + } + } + return name; +} + router.put("/:id/:action/:value?", async (req, res) => { const { action, id } = req.params, value = req.params.value ? req.params.value : ""; @@ -576,7 +614,15 @@ router.put("/:id/:action/:value?", async (req, res) => { addChatMessage(game, null, message); console.log(message); } - break + break; + case 'pass': + if (game.turn !== name) { + error = `You cannot pass when it isn't your turn.` + } + if (!error) { + game.turn = getNextPlayer(game, name); + addChatMessage(game, session, `${game.turn} passed their turn.`); + } case "state": const state = value; if (!state) {