@@ -1089,6 +1062,9 @@ class Table extends React.Component {
+
+ { development }
+
assetData.developmentCards.push({
+ type: 'progress',
+ card: card
+}));
+
+[ 'market', 'library', 'palace', 'university'].forEach(card => assetData.developmentCards.push({
+ type: 'vp',
+ card: card
+}));
+
const games = {};
const processTies = (players) => {
@@ -405,7 +413,8 @@ const getPlayer = (game, color) => {
wheat: 0,
sheep: 0,
wood: 0,
- brick: 0
+ brick: 0,
+ development: []
};
}
@@ -495,6 +504,12 @@ const loadGame = async (id) => {
}
}
+ for (let color in game.players) {
+ if (!game.players[color].development) {
+ game.players[color].development = [];
+ }
+ }
+
games[id] = game;
return game;
};
@@ -790,10 +805,13 @@ const getPrevPlayer = (game, name) => {
return name;
}
-const getValidCorners = (game, color) => {
+const getValidCorners = (game, color, type) => {
const limits = [];
- /* For each corner, if the corner already has a color set, skip it
+ /* For each corner, if the corner already has a color set, skip it if type
+ * isn't set. If type is set, if it is a match, and the color is a match,
+ * add it to the list.
+ *
* If we are limiting based on active player, a corner is only valid
* if it connects to a road that is owned by that player.
* If no color is set, walk each road that leaves that corner and
@@ -801,9 +819,18 @@ const getValidCorners = (game, color) => {
* If so, this location cannot have a settlement.
*/
layout.corners.forEach((corner, cornerIndex) => {
- if (game.placements.corners[cornerIndex].color) {
+ const placement = game.placements.corners[cornerIndex];
+ if (type) {
+ if (placement.color === color && placement.type === type) {
+ limits.push(cornerIndex);
+ }
return;
}
+
+ if (placement.color) {
+ return;
+ }
+
let valid;
if (!color) {
valid = true; /* Not filtering based on current player */
@@ -918,10 +945,13 @@ router.put("/:id/:action/:value?", async (req, res) => {
const name = session.name;
let message, index;
+ let corners, corner;
+
switch (action) {
case "roll":
error = roll(game, session);
break;
+
case "shuffle":
if (game.state !== "lobby") {
error = `Game no longer in lobby (${game.state}). Can not shuffle board.`;
@@ -936,6 +966,7 @@ router.put("/:id/:action/:value?", async (req, res) => {
console.log(message);
}
break;
+
case 'pass':
if (game.turn.name !== name) {
error = `You cannot pass when it isn't your turn.`
@@ -957,6 +988,7 @@ router.put("/:id/:action/:value?", async (req, res) => {
addChatMessage(game, session, `${name} passed their turn.`);
addChatMessage(game, null, `It is ${next}'s turn.`);
break;
+
case 'place-robber':
if (game.state !== 'normal' && game.turn.roll !== 7) {
error = `You cannot place robber unless 7 was rolled!`;
@@ -1004,6 +1036,7 @@ router.put("/:id/:action/:value?", async (req, res) => {
}
break;
+
case 'steal-resource':
if (game.turn.actions.indexOf('steal-resource') === -1) {
error = `You can only steal a resource when it is valid to do so!`;
@@ -1034,6 +1067,38 @@ router.put("/:id/:action/:value?", async (req, res) => {
}
game.turn.robberDone = true;
break;
+
+ case 'buy-development':
+ if (game.state !== 'normal') {
+ error = `You cannot purchase a development card unless the game is active.`;
+ break;
+ }
+ if (session.color !== game.turn.color) {
+ error = `It is not your turn! It is ${game.turn.name}'s turn.`;
+ break;
+ }
+ if (!game.turn.roll) {
+ error = `You cannot build until you have rolled.`;
+ break;
+ }
+ if (player.stone < 1 || player.wheat < 1 || player.sheep < 1) {
+ error = `You have insufficient resources to purchase a development card.`;
+ break;
+ }
+ if (game.developmentCards.length < 1) {
+ error = `There are no more development cards!`;
+ break;
+ }
+ if (game.turn.developmentPurchased) {
+ error = `You have already purchased a development card this turn.`;
+ }
+ addChatMessage(game, session, `Purchased a development card.`);
+ player.stone--;
+ player.wheat--;
+ player.sheep--;
+ player.development.push(game.developmentCards.pop());
+ break;
+
case 'buy-settlement':
if (game.state !== 'normal') {
error = `You cannot purchase a settlement unless the game is active.`;
@@ -1043,6 +1108,10 @@ router.put("/:id/:action/:value?", async (req, res) => {
error = `It is not your turn! It is ${game.turn.name}'s turn.`;
break;
}
+ if (!game.turn.roll) {
+ error = `You cannot build until you have rolled.`;
+ break;
+ }
if (player.brick < 1 || player.wood < 1 || player.wheat < 1 || player.sheep < 1) {
error = `You have insufficient resources to build a settlement.`;
break;
@@ -1051,20 +1120,16 @@ router.put("/:id/:action/:value?", async (req, res) => {
error = `You have already built all of your settlements.`;
break;
}
- let corners = getValidCorners(game, session.color);
+ corners = getValidCorners(game, session.color);
if (corners.length === 0) {
error = `There are no valid locations for you to place a settlement.`;
break;
}
- player.settlements--;
- player.brick--;
- player.wood--;
- player.wheat--;
- player.sheep--;
game.turn.actions = ['place-settlement'];
game.turn.limits = { corners };
- addChatMessage(game, session, `Purchased a settlement. Next, they need to place it.`);
+ addChatMessage(game, session, `${game.turn.name} is considering placing a settlement.`);
break;
+
case 'place-settlement':
if (game.state !== 'initial-placement' && game.state !== 'normal') {
error = `You cannot place an item unless the game is active.`;
@@ -1084,14 +1149,27 @@ router.put("/:id/:action/:value?", async (req, res) => {
error = `You tried to cheat! You should not try to break the rules.`;
break;
}
- const corner = game.placements.corners[index];
+ corner = game.placements.corners[index];
if (corner.color) {
error = `This location already has a settlement belonging to ${playerNameFromColor(game, corner.color)}!`;
break;
}
- corner.color = session.color;
- corner.type = 'settlement';
if (game.state === 'normal') {
+ if (player.brick < 1 || player.wood < 1 || player.wheat < 1 || player.sheep < 1) {
+ error = `You have insufficient resources to build a settlement.`;
+ break;
+ }
+ if (player.settlements < 1) {
+ error = `You have already built all of your settlements.`;
+ break;
+ }
+ player.settlements--;
+ player.brick--;
+ player.wood--;
+ player.wheat--;
+ player.sheep--;
+ corner.color = session.color;
+ corner.type = 'settlement';
game.turn.actions = [];
game.turn.limits = {};
addChatMessage(game, session, `${name} placed a settlement.`);
@@ -1099,11 +1177,92 @@ router.put("/:id/:action/:value?", async (req, res) => {
if (game.direction && game.direction === 'backward') {
session.initialSettlement = index;
}
+ corner.color = session.color;
+ corner.type = 'settlement';
game.turn.actions = ['place-road'];
game.turn.limits = { roads: layout.corners[index].roads }; /* road placement is limited to be near this corner */
addChatMessage(game, session, `Placed a settlement. Next, they need to place a road.`);
}
break;
+
+ case 'buy-city':
+ if (game.state !== 'normal') {
+ error = `You cannot purchase a city unless the game is active.`;
+ break;
+ }
+ if (session.color !== game.turn.color) {
+ error = `It is not your turn! It is ${game.turn.name}'s turn.`;
+ break;
+ }
+ if (!game.turn.roll) {
+ error = `You cannot build until you have rolled.`;
+ break;
+ }
+ if (player.wheat < 3 || player.stone < 2) {
+ error = `You have insufficient resources to build a city.`;
+ break;
+ }
+ if (player.city < 1) {
+ error = `You have already built all of your cities.`;
+ break;
+ }
+ corners = getValidCorners(game, session.color, 'settlement');
+ if (corners.length === 0) {
+ error = `There are no valid locations for you to place a city.`;
+ break;
+ }
+ game.turn.actions = ['place-city'];
+ game.turn.limits = { corners };
+ addChatMessage(game, session, `${game.turn.name} is considering upgrading a settlement to a city.`);
+ break;
+
+ case 'place-city':
+ if (game.state !== 'normal') {
+ error = `You cannot place an item unless the game is active.`;
+ break;
+ }
+ if (session.color !== game.turn.color) {
+ error = `It is not your turn! It is ${game.turn.name}'s turn.`;
+ break;
+ }
+ index = parseInt(value);
+ if (game.placements.corners[index] === undefined) {
+ error = `You have requested to place a city illegally!`;
+ break;
+ }
+ /* If this is not a placement the turn limits, discard it */
+ if (game.turn && game.turn.limits && game.turn.limits.corners && game.turn.limits.corners.indexOf(index) === -1) {
+ error = `You tried to cheat! You should not try to break the rules.`;
+ break;
+ }
+ corner = game.placements.corners[index];
+ if (corner.color !== session.color) {
+ error = `This location already has a settlement belonging to ${playerNameFromColor(game, corner.color)}!`;
+ break;
+ }
+ if (corner.type !== 'settlement') {
+ error = `This location already has a city!`;
+ break;
+ }
+ if (player.wheat < 3 || player.stone < 2) {
+ error = `You have insufficient resources to build a city.`;
+ break;
+ }
+ if (player.city < 1) {
+ error = `You have already built all of your cities.`;
+ break;
+ }
+ corner.color = session.color;
+ corner.type = 'city';
+ player.cities--;
+ player.settlements++;
+ player.wheat -= 3;
+ player.stone -= 2;
+ game.turn.actions = [];
+ game.turn.limits = {};
+ addChatMessage(game, session, `${name} upgraded a settlement to a city!`);
+ break;
+
case 'buy-road':
if (game.state !== 'normal') {
error = `You cannot purchase a road unless the game is active.`;
@@ -1113,6 +1272,10 @@ router.put("/:id/:action/:value?", async (req, res) => {
error = `It is not your turn! It is ${game.turn.name}'s turn.`;
break;
}
+ if (!game.turn.roll) {
+ error = `You cannot build until you have rolled.`;
+ break;
+ }
if (player.brick < 1 || player.wood < 1) {
error = `You have insufficient resources to build a road.`;
break;
@@ -1126,13 +1289,11 @@ router.put("/:id/:action/:value?", async (req, res) => {
error = `There are no valid locations for you to place a road.`;
break;
}
- player.roads--;
- player.brick--;
- player.wood--;
game.turn.actions = ['place-road'];
game.turn.limits = { roads };
- addChatMessage(game, session, `Purchased a road. Next, they need to place it.`);
+ addChatMessage(game, session, `${game.turn.name} is considering building a road.`);
break;
+
case 'place-road':
if (game.state !== 'initial-placement' && game.state !== 'normal') {
error = `You cannot place an item unless the game is active.`;
@@ -1157,13 +1318,25 @@ router.put("/:id/:action/:value?", async (req, res) => {
error = `This location already has a road belonging to ${playerNameFromColor(game, road.color)}!`;
break;
}
- road.color = session.color;
if (game.state === 'normal') {
+ if (player.brick < 1 || player.wood < 1) {
+ error = `You have insufficient resources to build a road.`;
+ break;
+ }
+ if (player.roads < 1) {
+ error = `You have already built all of your roads.`;
+ break;
+ }
+ player.roads--;
+ player.brick--;
+ player.wood--;
+ road.color = session.color;
game.turn.actions = [];
game.turn.limits = {};
addChatMessage(game, session, `${name} placed a road.`);
} else if (game.state === 'initial-placement') {
+ road.color = session.color;
addChatMessage(game, session, `${name} placed a road.`);
let next;
@@ -1228,9 +1401,7 @@ router.put("/:id/:action/:value?", async (req, res) => {
}
}
break;
- case 'place-city':
- error = `City placement not yet implemented!`;
- break;
+
case 'discard':
if (game.turn.roll !== 7) {
error = `You can only discard due to the Robber!`;
@@ -1258,6 +1429,7 @@ router.put("/:id/:action/:value?", async (req, res) => {
addChatMessage(game, null, `${session.name} must discard ${player.mustDiscard} more cards.`);
}
break;
+
case "state":
const state = value;
if (!state) {
@@ -1568,7 +1740,7 @@ const shuffleBoard = (game) => {
}
}
- shuffle(game.developmentCards)
+ shuffle(game.developmentCards);
}
/*