diff --git a/client/src/Resource.js b/client/src/Resource.js index ee0b3eb..3755b39 100644 --- a/client/src/Resource.js +++ b/client/src/Resource.js @@ -2,9 +2,9 @@ import React from "react"; import "./Resource.css"; import { assetsPath } from './Common.js'; -const Resource = ({ type, disabled, count }) => { +const Resource = ({ type, select, disabled, count }) => { const array = new Array(Number(count ? count : 0)); - const select = (event) => { + const click = select ? select : (event) => { if (!disabled) { event.target.classList.toggle('Selected'); } @@ -16,7 +16,7 @@ const Resource = ({ type, disabled, count }) => { { React.Children.map(array, i => (
)) } diff --git a/client/src/Table.js b/client/src/Table.js index 26bf52d..9e87a54 100755 --- a/client/src/Table.js +++ b/client/src/Table.js @@ -16,6 +16,7 @@ import Dice from './Dice.js'; import Resource from './Resource.js'; import ViewCard from './ViewCard.js'; import Winner from './Winner.js'; +import ChooseCard from './ChooseCard.js'; /* Start of withRouter polyfill */ // https://reactrouter.com/docs/en/v6/faq#what-happened-to-withrouter-i-need-it @@ -549,6 +550,7 @@ class Table extends React.Component { this.buildClicked = this.buildClicked.bind(this); this.closeCard = this.closeCard.bind(this); this.playCard = this.playCard.bind(this); + this.selectResource = this.selectResource.bind(this); this.mouse = { x: 0, y: 0 }; this.radius = 0.317; @@ -615,6 +617,10 @@ class Table extends React.Component { return this.sendAction('chat', undefined, {message: message}); } + selectResource(card) { + return this.sendAction('select-resource', card); + } + playCard(card) { this.setState({ cardActive: undefined }); return this.sendAction('play-card', undefined, card); @@ -1005,7 +1011,9 @@ class Table extends React.Component { render() { const game = this.state.game, - player = game ? game.player : undefined + player = game ? game.player : undefined, + isTurn = (game && game.turn && game.turn.color === game.color) ? true : false; + let color; switch (game ? game.color : undefined) { case "O": color = "orange"; break; @@ -1100,14 +1108,21 @@ class Table extends React.Component { } + { game + && isTurn + && game.turn.actions + && game.turn.actions.indexOf('select-resource') !== -1 && + + } + { game && game.state === 'normal' && game.turn && - game.turn.color === game.color && + isTurn && game.turn.actions && game.turn.actions.indexOf('steal-resource') !== -1 && } - { game && game.turn && game.turn.color !== game.color && + { game && game.turn && !isTurn && (game.state === 'initial-placement' || game.state === 'normal') && (!game.player || !game.player.mustDiscard) && } diff --git a/client/src/ViewCard.js b/client/src/ViewCard.js index 1790c80..62e8a2d 100644 --- a/client/src/ViewCard.js +++ b/client/src/ViewCard.js @@ -34,8 +34,20 @@ const ViewCard = ({table, card}) => { knight or one progress card.

, vp: <>1 victory point.

You only reveal your victory point cards when the game is over, either - when you or an opponent reaches 10+ victory points on their turn and declares + when you or an opponent + reaches 10+ victory points on their turn and declares victory!

, + 'progress-road-1': <> +

Play 2 new roads as if you had just built them.

+

This is still limited by the number of roads you have (a maximum of 15.)

+

NOTE: This card is not yet implemented. The server will give you 2 wood + and 2 brick and we trust you will use them to build 2 roads.

+

If + you do not have enough roads remaining, you may end up with extra resources... + but the game is in beta, so... be happy :) +

+

As an FYI, you currently have {15 - table.game.player.roads} roads remaining.

+ , 'progress-road-2': <>

Play 2 new roads as if you had just built them.

This is still limited by the number of roads you have (a maximum of 15.)

@@ -46,7 +58,17 @@ const ViewCard = ({table, card}) => { but the game is in beta, so... be happy :)

As an FYI, you currently have {15 - table.game.player.roads} roads remaining.

- + , + 'progress-monopoly': <> + When you play this card, you will select 1 type of resource. + All other players must give you all their resource cards of that type. + , + 'progress-year-of-plenty': <> + Take any 2 resources from the bank. Add them to your hand. They can be + 2 of the same resource or 2 different resources. +

Unfortunately the current implementation only lets you pick a single + resource and you will then get 2 of those.

+ }; let description; @@ -57,7 +79,7 @@ const ViewCard = ({table, card}) => { } if (description === undefined) { - console.log('No description for ', card); + console.log(`No description for ${card.type}-${card.card}`); } let canPlay = false; @@ -79,11 +101,15 @@ const ViewCard = ({table, card}) => { } if (canPlay) { canPlay = table.game.player.playedCard !== table.game.turns; + if (!canPlay) { + description = <>{description}

You have already played a development card this turn.

; + } } } if (card.played) { description = <>{description}

You have already played this card.

; + canPlay = false; } return ( diff --git a/server/routes/games.js b/server/routes/games.js index 4ebad75..3ab5df3 100755 --- a/server/routes/games.js +++ b/server/routes/games.js @@ -562,10 +562,12 @@ const adminActions = (game, action, value) => { break; case "give": - parts = value.match(/^([^-]+)-([0-9]+)$/); + parts = value.match(/^([^-]+)-(.*)$/); if (!parts) { return `Unable to parse give request.`; } + const type = parts[1], card = parts[2]; + for (let id in game.sessions) { if (game.sessions[id].name === game.turn.name) { session = game.sessions[id]; @@ -574,13 +576,31 @@ const adminActions = (game, action, value) => { if (!session) { return `Unable to determine current player turn to give resources.`; } - if (!(parts[1] in session.player)) { - return `Invalid resource request.`; + if (type in session.player) { + const count = parseInt(card); + session.player[card] += count; + addChatMessage(game, null, `Admin gave ${count} ${type} to ${game.turn.name}.`); + break; } - session.player[parts[1]] += parseInt(parts[2]); - addChatMessage(game, null, `Admin gave ${parseInt(parts[2])} ${parts[1]} to ${game.turn.name}.`); + + const index = game.developmentCards.findIndex(item => + item.card === card && item.type === type); + + if (index === -1) { + console.log({ card, type}, game.developmentCards); + return `Unable to find ${type}-${card} in the current deck of development cards.`; + } + let tmp = game.developmentCards.splice(index, 1)[0]; + tmp.turn = game.turns ? game.turns - 1 : 0; + session.player.development.push(tmp); + addChatMessage(game, null, `Admin gave a ${card}-${type} to ${game.turn.name}.`); break; + case "cards": + let results = game.developmentCards.map(card => `${card.type}-${card.card}`) + .join(', '); + return results; + case "roll": parts = value.match(/^([1-6])(-([1-6]))?$/); if (!parts) { @@ -1036,7 +1056,7 @@ const calculateRoadLengths = (game, session) => { let checkForTies = false; console.log(currentLongest, currentLength); - + if (currentLongest && game.players[currentLongest].longestRoad < currentLength) { addChatMessage(game, session, `${playerNameFromColor(game, currentLongest)} had their longest road split!`); checkForTies = true; @@ -1187,7 +1207,15 @@ const isCompatibleOffer = (player, offer) => { return false; } - console.log(player.gets, player.gives, offer); + console.log({ + player: getPlayerName(player), + gets: player.gets, + gives: player.gives + }, { + name: offer.name, + gets: offer.gets, + gives: offer.gives + }); player.gets.forEach(get => { if (!valid) { @@ -1242,6 +1270,17 @@ const isSameOffer = (player, offer) => { const checkOffer = (player, offer) => { let error = undefined; + + console.log({ + player: getPlayerName(player), + gets: player.gets, + gives: player.gives + }, { + name: offer.name, + gets: offer.gets, + gives: offer.gives + }); + offer.gives.forEach(give => { if (!error) { return; @@ -1397,7 +1436,6 @@ router.put("/:id/:action/:value?", async (req, res) => { /* Any player can reject an offer */ if (value === 'reject') { - const offer = req.body; session.player.offerRejected = true; addChatMessage(game, session, `${session.name} rejected ${game.turn.name}'s offer.`); break; @@ -1469,7 +1507,7 @@ router.put("/:id/:action/:value?", async (req, res) => { }); addChatMessage(game, session, `${session.name} has accepted a trade ` + - `offer for ${offerToString(session.player)} ` + + `offer to give ${offerToString(session.player)} ` + `from ${(offer.name === 'The bank') ? 'the bank' : offer.name}.`); delete game.turn.offer; @@ -1709,17 +1747,35 @@ router.put("/:id/:action/:value?", async (req, res) => { } } - if (card.type === 'progress' && card.card === 'road-2') { - addChatMessage(game, session, `${session.name} played a Road Building card. The server is giving them 2 brick and 2 wood to build those roads!`); - player.brick += 2; - player.wood += 2; + if (card.type === 'progress') { + switch (card.card) { + case 'road-1': + case 'road-2': + addChatMessage(game, session, `${session.name} played a Road Building card. The server is giving them 2 brick and 2 wood to build those roads!`); + player.brick += 2; + player.wood += 2; + break; + case 'monopoly': + game.turn.actions = [ 'select-resource' ]; + game.turn.active = 'monopoly'; + addChatMessage(game, session, `${session.name} played the Monopoly card.`); + break; + case 'year-of-plenty': + game.turn.actions = [ 'select-resource' ]; + game.turn.active = 'year-of-plenty'; + addChatMessage(game, session, `${session.name} played the Year of Plenty card.`); + break; + default: + addChatMessage(game, session, `Oh no! ${card.card} isn't impmented yet!`); + break; + } } card.played = true; player.playedCard = game.turns; - addChatMessage(game, session, `${session.name} played a ${card.type}-${card.card} development card.`); if (card.type === 'army') { player.army++; + addChatMessage(game, session, `${session.name} played a Kaniget!`); if (player.army > 2 && (!game.largestArmy || game.players[game.largestArmy].army < player.army)) { @@ -1736,6 +1792,72 @@ router.put("/:id/:action/:value?", async (req, res) => { break; + case 'select-resource': + if (!game || !game.turn || !game.turn.actions || + game.turn.actions.indexOf('select-resource') === -1) { + error = `Please, let's not cheat. Ok?`; + console.log(game); + break; + } + + if (session.color !== game.turn.color) { + error = `It is not your turn! It is ${game.turn.name}'s turn.`; + break; + } + + const type = value.trim(); + switch (type) { + case 'wheat': + case 'brick': + case 'sheep': + case 'stone': + case 'wood': + break; + default: + error = `That is not a valid resource type!`; + break; + }; + if (error) { + break; + } + addChatMessage(game, session, `${session.name} has chosen ${type}!`); + + switch (game.turn.active) { + case 'monopoly': + const gave = []; + let total = 0; + for (let color in game.players) { + const player = game.players[color]; + if (player.status === 'Not active') { + continue + } + if (color === session.color) { + continue; + } + if (player[type]) { + gave.push(`${playerNameFromColor(game, color)} gave ${player[type]} ${type}`); + session.player[type] += player[type]; + total += player[type]; + player[type] = 0; + } + } + + if (gave.length) { + addChatMessage(game, session, `Players ${gave.join(', ')}. In total, ${session.name} received ${total} ${type}.`); + } else { + addChatMessage(game, session, 'No players had that resource. Wa-waaaa.'); + } + break; + + case 'year-of-plenty': + session.player[type] += 2; + addChatMessage(game, session, `${session.name} received 2 ${type} from the bank.`); + break; + } + delete game.turn.active; + game.turn.actions = []; + break; + case 'buy-settlement': if (game.state !== 'normal') { error = `You cannot purchase a settlement unless the game is active.`; @@ -2197,10 +2319,8 @@ const debugChat = (game, preamble) => { } playerInventory += ` ${playerNameFromColor(game, key)} has `; const has = [ 'wheat', 'brick', 'sheep', 'stone', 'wood' ].map(resource => { - if (game.players[key][resource] > 0) { - return `${game.players[key][resource]} ${resource}`; - } - return ''; + const count = game.players[key][resource] ? game.players[key][resource] : 0; + return `${count} ${resource}`; }).filter(item => item !== '').join(', '); if (has) { playerInventory += `${has}, `; @@ -2276,7 +2396,6 @@ const sendGame = async (req, res, game, error) => { lastTime = message.date; }); - /* Calculate points and determine if there is a winner */ for (let key in game.players) { const player = game.players[key];