+
{ roads }
> }
diff --git a/client/src/Table.js b/client/src/Table.js
index 01cb15e..5fec9ff 100755
--- a/client/src/Table.js
+++ b/client/src/Table.js
@@ -281,8 +281,8 @@ const StartButton = ({ table }) => {
const WaitingForPlayer = ({table}) => {
return (
- { table.game &&
- Waiting for {table.game.turn} to complete their turn.
+ { table.game && table.game.turn &&
+ Waiting for {table.game.turn.name} to complete their turn.
}
);
@@ -376,7 +376,7 @@ const Action = ({ table }) => {
> }
{ table.game.state === 'active' && <>
-
+
> }
{ !inLobby &&
@@ -1069,7 +1069,8 @@ class Table extends React.Component {
}
- { game && game.turn && game.turn !== game.name &&
+ { game && game.turn && game.turn.color !== game.color &&
+ (game.state === 'initial-placement' || game.state === 'normal') &&
}
diff --git a/server/routes/games.js b/server/routes/games.js
index 8ba59aa..5624fba 100755
--- a/server/routes/games.js
+++ b/server/routes/games.js
@@ -6,6 +6,8 @@ const express = require("express"),
fs = require("fs"),
accessSync = fs.accessSync,
randomWords = require("random-words");
+
+const layout = require('./layout.js');
let gameDB;
@@ -34,267 +36,6 @@ function shuffle(array) {
return array;
}
-/* Board Tiles:
- * 0 1 2
- * 3 4 5 6
- * 7 8 9 10 11
- * 12 13 14 15
- * 16 17 18
- */
-
-/*
- * c0
- * /\
- * r0 / \r1
- * c6 / \ c1
- * | |
- * r6| p,a | r2
- * c5| | c3
- * \ /
- * r5 \ / r3
- * \/
- * c4
-*/
-
-/* |
-* 1 2 |
-* 0 1| 3| 5|
-* \. / \ / \ / \ 3
-* \. 0/ 1\ 3/ 4\ 6/ 7\
-* \./ \ / \ / \
-* 0| 2| 4| |6
-* 17 2| 0 5| 1 8| 2 |9 4
-* 8| 10| 12| |14
-* / \ / \ / \ / \
-* 10/ 11\ 13/ 14\ 16/ 17\ 19/ 20\
-* / \ / \ / \ / \
-* 7| 9| 11| 13| |15
-* 16 12| 3 15| 4 18| 5 21| 6 |22
-* 17| 19| 21| 23| |25 5
-* / \ / \ / \ / \ / \ ,/
-* 23/ 24\ 26/ 27\ 29/ 30\ 32/ 33\ 35/ 36\ ,/
-* / \ / \ / \ / \ / \ ,/
-* 16| 18| 20| 22| 24| |26
-* 15 25| 7 28| 8 31| 9 34| 10 37| 11 |38 6
-* 27| 29| 31| 33| 35| |37
-* /' \ / \ / \ / \ / \ /
-* /' 39\ 40/ 41\ 43/ 44\ 46/ 47\ 49/ 50\ /53
-* /' \ / \ / \ / \ / \ / 7
-* 28| 30| 32| 34| |36
-* 14 42| 12 45| 13 48| 14 51| 15 |52
-* 38| 40| 42| 44| |46
-* \ / \ / \ / \ /
-* 54\ 55/ 56\ 58/ 59\ 61/ 62\ /65
-* \ / \ / \ / \ / 8
-* 39| 41| 43| |45
-* 13 57| 16 60| 17 63| 18 |64
-* 47| 49| 51| |53
-* \ / \ / \ / `\
-* 66\ 67/ 68\ 69/ 70\ /71 `\
-* \ / \ / \ / `\
-* 48| 50| 52| 9
-* |
-* 12 | 11 10
-*/
-const Tile = (corners, roads) => {
- return {
- corners: corners, /* 6 */
- pip: -1,
- roads: roads,
- asset: -1
- };
-};
-
-/* Borders have three sections each, so they are numbered
- * 0-17 clockwise. Some corners share two borders. */
-
-const Corner = (roads, banks) => {
- return {
- roads: roads, /* max of 3 */
- banks: banks, /* max of 2 */
- data: undefined
- };
-};
-
-const Road = (corners) => {
- return {
- corners: corners, /* 2 */
- data: undefined
- }
-};
-
-const layout = {
- tiles: [
- Tile([ 0, 1, 2, 10, 9, 8], [ 0, 1, 5, 13, 11, 2]),
- Tile([ 2, 3, 4, 12, 11, 10], [ 3, 4, 8, 16, 14, 5]),
- Tile([ 4, 5, 6, 14, 13, 12], [ 6, 7, 9, 19, 17, 8]),
-
- Tile([ 7, 8, 9, 19, 18, 17], [ 10, 11, 15, 26, 24, 12]),
- Tile([ 9, 10, 11, 21, 20, 19], [ 13, 14, 18, 29, 27, 15]),
- Tile([ 11, 12, 13, 23, 22, 21], [ 16, 17, 21, 32, 30, 18]),
- Tile([ 13, 14, 15, 25, 24, 23], [ 19, 20, 22, 35, 33, 21]),
-
- Tile([ 16, 17, 18, 29, 28, 27], [ 23, 24, 28, 40, 39, 25]),
- Tile([ 18, 19, 20, 31, 30, 29], [ 26, 27, 31, 43, 41, 28]),
- Tile([ 20, 21, 22, 33, 32, 31], [ 29, 30, 34, 46, 44, 31]),
- Tile([ 22, 23, 24, 35, 34, 33], [ 32, 33, 37, 49, 47, 34]),
- Tile([ 24, 25, 26, 37, 36, 35], [ 35, 36, 38, 53, 50, 37]),
-
- Tile([ 28, 29, 30, 40, 39, 38], [ 40, 41, 45, 55, 54, 42]),
- Tile([ 30, 31, 32, 42, 41, 40], [ 43, 44, 48, 58, 56, 45]),
- Tile([ 32, 33, 34, 44, 43, 42], [ 46, 47, 51, 61, 59, 48]),
- Tile([ 34, 35, 36, 46, 45, 44], [ 49, 50, 52, 65, 62, 51]),
-
- Tile([ 39, 40, 41, 49, 48, 47], [ 55, 56, 60, 67, 66, 57]),
- Tile([ 41, 42, 43, 51, 50, 49], [ 58, 59, 63, 69, 68, 60]),
- Tile([ 43, 44, 45, 53, 52, 51], [ 61, 62, 64, 71, 70, 63])
- ],
- roads: [
- /* 0 */
- Road([0, 1]),
- Road([1, 2]),
- Road([0, 8]),
- Road([2, 3]),
- Road([3, 4]),
- Road([2, 10]),
- Road([4, 5]),
- Road([5, 6]),
- Road([4, 12]),
- Road([6, 14]),
- /* 10 */
- Road([8, 7]),
- Road([8, 9]),
- Road([7, 17]),
- Road([9, 10]),
- Road([10, 11]),
- Road([9, 19]),
- Road([12, 11]),
- Road([12, 13]),
- Road([11, 21]),
- Road([14, 13]),
- /* 20 */
- Road([14, 15]),
- Road([13,23 ]),
- Road([15, 25]),
- Road([17, 16]),
- Road([17, 18]),
- Road([16, 27]),
- Road([19, 18]),
- Road([19, 20]),
- Road([18, 29]),
- Road([21, 20]),
- /* 30 */
- Road([21, 22]),
- Road([20, 31]),
- Road([23, 22]),
- Road([23, 24]),
- Road([22,33]),
- Road([25,24]),
- Road([25,26]),
- Road([24, 35]),
- Road([26,37]),
- Road([27,28]),
- /* 40 */
- Road([29,28]),
- Road([29,30]),
- Road([28,38]),
- Road([31,30]),
- Road([31,32]),
- Road([30,40]),
- Road([33,32]),
- Road([33,34]),
- Road([32,42]),
- Road([35,34]),
- /* 50 */
- Road([35,36]),
- Road([34,44]),
- Road([36,46]),
- Road([37,36]),
- Road([38,39]),
- Road([40,39]),
- Road([40,41]),
- Road([39,47]),
- Road([41,42]),
- Road([42,43]),
- /* 60 */
- Road([41,49]),
- Road([44,43]),
- Road([44,45]),
- Road([43,51]),
- Road([45,53]),
- Road([46,45]),
- Road([47,48]),
- Road([49,48]),
- Road([49,50]),
- Road([51,50]),
- /* 70 */
- Road([51,52]),
- Road([53,52]),
- ],
- corners: [
- /* 0 */
- /* 0 */ Corner([2, 0],[17,0]),
- /* 1 */ Corner([0, 1],[0,1]),
- /* 2 */ Corner([1,3,5],[1]),
- /* 3 */ Corner([3,4],[1,2]),
- /* 4 */ Corner([8,4,6],[2]),
- /* 5 */ Corner([6,7],[2,3]),
- /* 6 */ Corner([7,9],[3,4]),
- /* 7 */ Corner([12,10],[16,17]),
- /* 8 */ Corner([2,10,11],[17]),
- /* 9 */ Corner([11,13,15],[]),
- /* 10 */
- /* 10 */ Corner([5,13,14],[]),
- /* 11 */ Corner([14,16,18],[]),
- /* 12 */ Corner([8,16,17],[]),
- /* 13 */ Corner([17,19,21],[]),
- /* 14 */ Corner([9,19,20],[4]),
- /* 15 */ Corner([20,22],[4,5]),
- /* 16 */ Corner([23,25],[16,15]),
- /* 17 */ Corner([12,23,24],[16]),
- /* 18 */ Corner([24,26,28],[]),
- /* 19 */ Corner([15,26,27],[]),
- /* 20 */
- /* 20 */ Corner([27,29,31],[]),
- /* 21 */ Corner([18,29,30],[]),
- /* 22 */ Corner([30,32,34],[]),
- /* 23 */ Corner([21,32,33],[]),
- /* 24 */ Corner([33,35,37],[]),
- /* 25 */ Corner([22,35,36],[5]),
- /* 26 */ Corner([36,38],[5,6]),
- /* 27 */ Corner([25,39],[15,14]),
- /* 28 */ Corner([39,40,42],[14]),
- /* 29 */ Corner([28,40,41],[]),
- /* 30 */
- /* 30 */ Corner([41,43,45],[]),
- /* 31 */ Corner([31,43,44],[]),
- /* 32 */ Corner([44,46,48],[]),
- /* 33 */ Corner([34,46,47],[]),
- /* 34 */ Corner([47,49,51],[]),
- /* 35 */ Corner([37,49,50],[]),
- /* 36 */ Corner([50,53,52],[7]),
- /* 37 */ Corner([38,53],[6,7]),
- /* 38 */ Corner([42,54],[14,13]),
- /* 39 */ Corner([54,55,57],[13]),
- /* 40 */
- /* 40 */ Corner([45,55,56],[]),
- /* 41 */ Corner([56,58,60],[]),
- /* 42 */ Corner([48,58,59],[]),
- /* 43 */ Corner([59,61,63],[]),
- /* 44 */ Corner([51,61,62],[]),
- /* 45 */ Corner([62,65,64],[8]),
- /* 46 */ Corner([52,65],[7,8]),
- /* 47 */ Corner([57,66],[13,12]),
- /* 48 */ Corner([67,66],[12,11]),
- /* 49 */ Corner([60,67,68],[11]),
- /* 50 */
- /* 50 */ Corner([68,69],[11,10]),
- /* 51 */ Corner([69,70,63],[10]),
- /* 52 */ Corner([70,71],[10,9]),
- /* 53 */ Corner([64,71],[8,9]),
- ]
-}
-
const assetData = {
tiles: [
{ type: "desert", card: 0 },
@@ -453,11 +194,15 @@ const processGameOrder = (game, player, dice) => {
}).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]);
+ game.state = 'initial-placement';
+ message = `Initial settlement placement has started!`;
+ game.turn = {
+ actions: ['place-settlement'],
+ name: getPlayerName(game, players[0]),
+ color: getPlayerColor(game, players[0])
+ };
addChatMessage(game, null, message);
- message = `It is ${game.turn}'s turn.`;
+ message = `It is ${game.turn.name}'s turn.`;
} else {
message = `There are still ties for player order!`;
}
@@ -588,6 +333,18 @@ const loadGame = async (id) => {
});
}
+ if (game.state === 'active') {
+ game.state = 'initial-placement';
+ }
+
+ if (typeof game.turn !== 'object') {
+ delete game.turn;
+ }
+
+ if (!game.status) {
+ resetGame(game);
+ }
+
/* Reconnect session player colors to the player objects */
for (let id in game.sessions) {
const session = game.sessions[id];
@@ -618,12 +375,7 @@ const adminActions = (game, action, value) => {
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;
- }
- delete game.turn;
+ resetGame(game);
game.state = 'game-order';
break;
}
@@ -792,6 +544,15 @@ const addChatMessage = (game, session, message) => {
});
};
+const getColorFromName = (game, name) => {
+ for (let id in game.sessions) {
+ if (game.sessions[id].name === name) {
+ return color;
+ }
+ }
+ return '';
+};
+
const getNextPlayer = (game, name) => {
let color;
for (let id in game.sessions) {
@@ -881,10 +642,48 @@ router.put("/:id/:action/:value?", async (req, res) => {
error = `You cannot pass when it isn't your turn.`
}
if (!error) {
- game.turn = getNextPlayer(game, name);
+ const next = getNextPlayer(game, name);
+ game.turn = {
+ actions: game.turn.actions,
+ name: next,
+ color: getColorFromName(game, next)
+ };
addChatMessage(game, session, `${name} passed their turn.`);
- addChatMessage(game, null, `It is ${game.turn}'s turn.`);
+ addChatMessage(game, null, `It is ${next}'s turn.`);
}
+ case 'place-settlement':
+ if (game.state !== 'initial-placement' || game.state !== 'normal') {
+ error = `You cannot place an item unless the game is active.`;
+ break;
+ }
+ if (session.color !== game.turn) {
+ error = `It is not your turn!`;
+ break;
+ }
+ const index = value;
+ if (game.corners[index] === undefined) {
+ error = `You have requested to place a settlement illegally!`;
+ break;
+ }
+ const corner = game.corners[index];
+ if (corner.color) {
+ error = `This location already has a settlement belonging to ${playerNameFromColor(game, corner.color)}!`;
+ break;
+ }
+ corner.color = game.color;
+ corner.type = 'settlement';
+ if (game.state === 'initial-placement') {
+ game.turn.actions = ['place-road'];
+ game.turn.limits = { corner: index }; /* road placement is limited to be near this corner index */
+ addChatMessage(game, session, `Placed a settlement. Next, they need to place a road.`);
+ }
+ break;
+ case 'place-road':
+ error = `Road placement not yet implemented!`;
+ break;
+ case 'place-city':
+ error = `City placement not yet implemented!`;
+ break;
case "state":
const state = value;
if (!state) {
@@ -903,11 +702,7 @@ router.put("/:id/:action/:value?", async (req, res) => {
break;
}
- for (let key in game.players) {
- game.players[key].order = 0;
- delete game.players[key].orderRoll;
- delete game.players[key].orderStatus;
- }
+ resetGame(game);
message = `${name} requested to start the game.`;
addChatMessage(game, null, message);
@@ -1016,6 +811,39 @@ const sendGame = async (req, res, game, error) => {
return res.status(200).send(playerGame);
}
+const resetGame = (game) => {
+ delete game.turn;
+
+ game.state = 'lobby';
+
+ game.status = {
+ corners: [],
+ roads: []
+ };
+
+ for (let i = 0; i < layout.corners.length; i++) {
+ game.status.corners[i] = {
+ color: undefined,
+ type: undefined
+ };
+ }
+
+ for (let i = 0; i < layout.roads.length; i++) {
+ game.status.roads[i] = {
+ color: undefined,
+ type: undefined
+ };
+ }
+
+ for (let key in game.players) {
+ game.players[key].order = 0;
+ delete game.players[key].orderRoll;
+ delete game.players[key].orderStatus;
+ }
+
+ delete game.turn;
+}
+
const createGame = (id) => {
/* Look for a new game with random words that does not already exist */
while (!id) {
@@ -1061,6 +889,8 @@ const createGame = (id) => {
game[field] = assetData[field]
});
+ resetGame(game);
+
games[game.id] = game;
shuffleBoard(game);
console.log(`New game created: ${game.id}`);