diff --git a/client/src/Table.css b/client/src/Table.css index 74bee7d..b7567d8 100755 --- a/client/src/Table.css +++ b/client/src/Table.css @@ -8,6 +8,53 @@ background-image: url("./assets/tabletop.png"); } +.GameOrder { + display: flex; + position: absolute; + left: 0; + right: 0; + bottom: 0; + top: 0; + justify-content: center; + align-items: center; + background: rgba(0,0,0,0.5); +} + +.GameOrder .Title { + align-self: center; + padding: 2px; + font-weight: bold; +} + +.GameOrder .PlayerList { + padding: 0.5em; + background-color:rgba(224, 224, 224); + margin: 0.5em 0; +} +.GameOrder > * { + width: 20em; + display: inline-flex; + padding: 0.5em; + flex-direction: column; +} + +.GameOrder .PlayerColor { + width: 1em; + height: 1em; +} + +.GameOrder .GameOrderPlayer { + display: flex; + flex-direction: row; + width: 100%; + align-items: center; + padding: 2px 0; +} + +.GameOrderPlayer > * { + margin: 0 0.25em; +} + .Display { display: inline-block; position: absolute; @@ -42,7 +89,6 @@ box-sizing: border-box; max-height: 100%; max-width: 100%; - opacity: 0.7; } .Stack { @@ -81,7 +127,6 @@ max-width: 40vw; z-index: 100; padding: 0.5em; - opacity: 0.7; } .Game > * { diff --git a/client/src/Table.js b/client/src/Table.js index 5761ec1..1054444 100755 --- a/client/src/Table.js +++ b/client/src/Table.js @@ -72,14 +72,14 @@ const useStyles = makeStyles((theme) => ({ const Dice = ({ pips }) => { let name; - switch (pips) { - case 1: name = 'one'; break; - case 2: name = 'two'; break; - case 3: name = 'three'; break; - case 4: name = 'four'; break; - case 5: name = 'five'; break; + switch (pips.toString()) { + case '1': name = 'one'; break; + case '2': name = 'two'; break; + case '3': name = 'three'; break; + case '4': name = 'four'; break; + case '5': name = 'five'; break; default: - case 6: name = 'six'; break; + case '6': name = 'six'; break; } return ( {name} @@ -271,6 +271,61 @@ const StartButton = ({ table }) => { ); }; +const GameOrder = ({table}) => { + + const rollClick = (event) => { + table.throwDice(); + } + + if (!table.game) { + return (<>); + } + + let players = []; + for (let color in table.game.players) { + const item = table.game.players[color], + name = getPlayerName(table.game.sessions, color); + if (name) { + if (!item.orderRoll) { + item.orderRoll = 0; + } + players.push({ name: name, color: color, ...item }); + } + } + + players.sort((A, B) => { + if (A.order === B.order) { + if (A.orderRoll === B.orderRoll) { + return A.name.localeCompare(B.name); + } + return B.orderRoll - A.orderRoll; + } + return B.order - A.order; + }); + + console.log(players); + players = players.map(item => +
+ +
{item.order+1}. {item.name}
+ { item.orderRoll !== 0 && <>rolled . } + { item.orderRoll === 0 && <>has not rolled yet.} +
+ ); + + return ( +
+ { table.game && +
Game Order
+
+ { players } +
+ +
} +
+ ); +}; + const Action = ({ table }) => { const newTableClick = (event) => { return table.shuffleTable(); @@ -299,8 +354,6 @@ const Action = ({ table }) => { } - { table.game.state === 'game-order' && - } { table.game.state === 'active' && <> @@ -952,7 +1005,7 @@ class Table extends React.Component { const game = this.state.game; return ( -
this.el = el}> +
{ game &&
{ this.state.message } @@ -963,8 +1016,11 @@ class Table extends React.Component { }
} + { game && game.state === 'game-order' && + + } -
this.cards = el}> +
{ game && game.state === "active" && <>
In hand
diff --git a/server/app.js b/server/app.js index 271a76b..381265a 100755 --- a/server/app.js +++ b/server/app.js @@ -15,6 +15,7 @@ const express = require("express"), require("./console-line.js"); /* Monkey-patch console.log with line numbers */ + const frontendPath = config.get("frontendPath").replace(/\/$/, "") + "/", serverConfig = config.get("server"); @@ -46,8 +47,9 @@ app.use(bodyParser.urlencoded({ * This runs before after cookie parsing, but before routes. If we set * immediate: true on the morgan options, it happens before cookie parsing * */ +const logging = false; - morgan.token('remote-user', function (req) { +if (logging) morgan.token('remote-user', function (req) { return req.user ? req.user.username : "N/A"; }); @@ -56,7 +58,7 @@ const logSkipPaths = new RegExp("^" + basePath + "(" + [ ".*thumbs\\/", "bower_components", ].join(")|(") + ")"); -app.use(morgan('common', { +if (logging) app.use(morgan('common', { skip: function (req) { return logSkipPaths.exec(req.originalUrl); } @@ -276,6 +278,12 @@ app.use(basePath, index); app.set("port", serverConfig.port); const server = require("http").createServer(app); +process.on('SIGINT', () => { + server.close(() => { + console.log("Gracefully shutting down from SIGINT (Ctrl-C)"); + process.exit(1); + }); +}); require("./db/games").then(function(db) { gameDB = db; diff --git a/server/routes/games.js b/server/routes/games.js index c38e230..f4ecbbd 100755 --- a/server/routes/games.js +++ b/server/routes/games.js @@ -99,6 +99,90 @@ for (let i = 0; i < 5; i++) { const games = {}; +const processTies = (players) => { + players.sort((A, B) => { + if (A.order === B.order) { + return B.orderRoll - A.orderRoll; + } + return A.order - B.order; + }); + + /* Sort the players into buckets based on their + * order, and their current roll. If a resulting + * roll array has more than one element, then there + * is a tie that must be resolved */ + + let slots = []; + players.forEach(player => { + if (!slots[player.order]) { + slots[player.order] = []; + } + if (!(player.orderRoll in slots[player.order])) { + slots[player.order][player.orderRoll] = []; + } + slots[player.order][player.orderRoll].push(player); + }); + + let ties = false, order = 0; + slots.forEach((slot) => { + slot.forEach(pips => { + if (pips.length !== 1) { + ties = true; + pips.forEach(player => { + player.orderRoll = 0; + player.order = order; + player.orderStatus = `Tied for ${order}`; + }); + } else { + pips[0].order = order; + pips[0].orderStatus = `Placed in ${order}`; + } + order += pips.length + }) + }); + + return !ties; +} + +const processGameOrder = (game, player, dice) => { + let message; + + console.log(`rolled ${dice}`); + player.orderRoll = dice; + + let players = []; + + let doneRolling = true; + + for (let key in game.players) { + const tmp = game.players[key]; + if (tmp.status === 'Not active') { + continue; + } + if (!tmp.orderRoll) { + doneRolling = false; + } + tmp.color = key; + players.push(tmp); + } + + /* If 'doneRolling' is TRUE then everyone has rolled */ + if (doneRolling) { + if (processTies(players)) { + message = `Player order set to ${players.map(player => player.color).join(',')}.`; + game.chat.push({ date: Date.now(), message: message }); + game.state = 'active' + message = `Game has started!`; + } else { + message = `There are still ties for player order!`; + } + } + + if (message) { + game.chat.push({ date: Date.now(), message: message }); + } +} + const roll = (game, session) => { let message, error; @@ -110,14 +194,18 @@ const roll = (game, session) => { error = `Rolling dice in the lobby is not allowed!`; case "game-order": - if (player.order) { - error = `Player ${name} already rolled for order.`; + if (!player) { + error = `This player is not active!`; break; } + + if (player.order || player.orderRoll) { + error = `Player ${name} has already rolled for player order.`; + // break; + } game.dice = [ Math.ceil(Math.random() * 6) ]; - player.order = game.dice[0]; - message = `${name} rolled ${game.dice[0]} for play order.`; + processGameOrder(game, player, game.dice[0]); break; case "active": @@ -231,12 +319,45 @@ const clearPlayer = (player) => { player.status = 'Not active'; player.lastActive = 0; player.order = 0; + delete player.orderRoll; + delete player.orderStatus; } const adminActions = (game, action, value) => { + let color, player; + switch (action) { + case "state": + switch (value) { + case 'game-order': + for (let key in game.players) { + game.players[key].order = 0; + delete game.players[key].orderRoll; + delete game.players[key].orderStatus; + } + game.state = 'game-order'; + break; + } + break; + + case "roll": + let dice = value.replace(/.*-/, ''); + switch (value.replace(/-.*/, '')) { + case 'orange': color = 'O'; break; + case 'red': color = 'R'; break; + case 'blue': color = 'B'; break; + case 'white': color = 'W'; break; + } + if (!color) { + return `Unable to find player ${value.replace(/-.*/, '')}` + } + + player = game.players[color]; + processGameOrder(game, player, dice); + + break; + case "kick": - let color; switch (value) { case 'orange': color = 'O'; break; case 'red': color = 'R'; break; @@ -247,7 +368,7 @@ const adminActions = (game, action, value) => { return `Unable to find player ${value}` } - const player = game.players[color]; + player = game.players[color]; for (let id in game.sessions) { const session = game.sessions[id]; if (session.player !== player) { @@ -459,7 +580,7 @@ router.put("/:id/:action/:value?", async (req, res) => { router.get("/:id", async (req, res/*, next*/) => { const { id } = req.params; - console.log("GET games/" + id); +// console.log("GET games/" + id); let game = await loadGame(id); if (game) { @@ -684,7 +805,7 @@ const shuffleBoard = (game) => { * pip value to the robber (18) otherwise set * the target pip value to the currently incremeneting * pip value. */ - if (game.tiles[game.tileOrder[target]].type === 'robber') { + if (game.tiles[game.tileOrder[target]].type === 'desert') { game.pipOrder[target] = 18; } else { game.pipOrder[target] = p++;