diff --git a/client/src/App.js b/client/src/App.js index b48cec6..76ab63b 100755 --- a/client/src/App.js +++ b/client/src/App.js @@ -291,30 +291,23 @@ const Table = () => { } - { warning &&
{ warning }
} + { warning &&
+ { warning } +
} { state === 'normal' && } { color && state === 'game-order' && } + { /* state === 'normal' && */ } + { /* state === 'winner' && */ } + { /* cardActive && */ } + + { /*isTurn && turn && turn.actions && game.turn.actions.indexOf('select-resources') !== -1 && + + */ } - { /* state === 'winner' && - - } - - { state === 'normal' && - turn && turn.actions && turn.actions.indexOf('trade') !== -1 && - - } - - { cardActive && - - } - - { isTurn && turn && turn.actions && game.turn.actions.indexOf('select-resources') !== -1 && - - */ }
{ name !== "" && } diff --git a/client/src/Trade.js b/client/src/Trade.js index e7fb9f7..189b8c9 100644 --- a/client/src/Trade.js +++ b/client/src/Trade.js @@ -16,7 +16,11 @@ const empty = { sheep: 0 }; -const Trade = ({table}) => { +/* +&& turn && turn.actions + && turn.actions.indexOf('trade') !== -1 && +*/ +const Trade = () => { const [gives, setGives] = useState(Object.assign({}, empty)); const [gets, setGets] = useState(Object.assign({}, empty)); const player = (table.game && table.game.player) ? table.game.player : undefined; diff --git a/server/routes/games.js b/server/routes/games.js index 1d0d209..8cabe53 100755 --- a/server/routes/games.js +++ b/server/routes/games.js @@ -808,7 +808,7 @@ const setPlayerName = (game, session, name) => { rejoin = true; /* Update the session object from tmp, but retain websocket * from active session */ - Object.assign(session, tmp, { ws: session.ws }); + Object.assign(session, tmp, { ws: session.ws, id: session.id }); console.log(`${info}: ${name} has been reallocated to a new session.`); delete game.sessions[id]; } else { @@ -1252,7 +1252,7 @@ const calculateRoadLengths = (game, session) => { } }); - console.log('Graphs A:', graphs); + if (debug.road) console.log('Graphs A:', graphs); clearRoadWalking(game); graphs.forEach(graph => { @@ -1268,9 +1268,9 @@ const calculateRoadLengths = (game, session) => { }); }); - console.log('Graphs B:', graphs); + if (debug.road) console.log('Graphs B:', graphs); - console.log('Pre update:', game.placements.roads.filter(road => road.color)); + if (debug.road) console.log('Pre update:', game.placements.roads.filter(road => road.color)); for (let color in game.players) { if (game.players[color] === 'Not active') { @@ -1292,11 +1292,11 @@ const calculateRoadLengths = (game, session) => { game.placements.roads.forEach(road => delete road.walking); - console.log('Post update:', game.placements.roads.filter(road => road.color)); + if (debug.road) console.log('Post update:', game.placements.roads.filter(road => road.color)); let checkForTies = false; - console.log(currentLongest, currentLength); + if (debug.road) console.log(currentLongest, currentLength); if (currentLongest && game.players[currentLongest].longestRoad < currentLength) { const _session = sessionFromColor(game, currentLongest); @@ -1320,7 +1320,7 @@ const calculateRoadLengths = (game, session) => { } } - console.log({ longestPlayers }); + if (debug.road) console.log({ longestPlayers }); if (longestPlayers.length > 0) { if (longestPlayers.length === 1) { @@ -1344,8 +1344,6 @@ const calculateRoadLengths = (game, session) => { game.longestRoad = null; game.longestRoadLength = 0; } - - }; const getValidCorners = (game, color, type) => { @@ -1667,7 +1665,7 @@ router.put("/:id/:action/:value?", async (req, res) => { return res.status(400).send(error); }); -const trade = (game, session, { offer, value }) => { +const trade = (game, session, action, offer) => { const name = session.name; if (game.state !== "normal") { @@ -1691,7 +1689,7 @@ const trade = (game, session, { offer, value }) => { } /* Only the active player can cancel trading */ - if (value === 'cancel') { + if (action === 'cancel') { /* TODO: Perhaps 'cancel' is how a player can remove an offer... */ if (game.turn.name !== name) { return `Only the active player can cancel trading negotiations.`; @@ -1703,7 +1701,7 @@ const trade = (game, session, { offer, value }) => { } /* Any player can make an offer */ - if (value === 'offer') { + if (action === 'offer') { error = checkPlayerOffer(game, session.player, offer); if (error) { return error; @@ -1746,7 +1744,7 @@ const trade = (game, session, { offer, value }) => { } /* Any player can reject an offer */ - if (value === 'reject') { + if (action === 'reject') { /* If the active player rejected an offer, they rejected another player */ const other = game.players[offer.color]; if (!other.offerRejected) { @@ -1758,7 +1756,7 @@ const trade = (game, session, { offer, value }) => { } /* Only the active player can accept an offer */ - if (value === 'accept') { + if (action === 'accept') { if (game.turn.name !== name) { return `Only the active player can accept an offer.`; } @@ -2000,7 +1998,9 @@ const stealResource = (game, session, color) => { }); } -const buyDevelopment = (game, session, value) => { +const buyDevelopment = (game, session) => { + const player = session.player; + if (game.state !== 'normal') { return `You cannot purchase a development card unless the game is active.`; } @@ -2052,8 +2052,8 @@ const buyDevelopment = (game, session, value) => { }); } -const playCard = (game, session, { card }) => { - const name = session.name; +const playCard = (game, session, card) => { + const name = session.name, player = session.player; if (game.state !== 'normal') { return `You cannot play a development card unless the game is active.`; @@ -2444,7 +2444,9 @@ const placeRoad = (game, session, index) => { game.state = 'normal'; } } - + sendUpdateToPlayer(session, { + private: game.player + }); sendUpdateToPlayers(game, { placements: game.placements, turn: game.turn, @@ -2453,9 +2455,7 @@ const placeRoad = (game, session, index) => { longestRoad: game.longestRoad, longestRoadLength: game.longestRoadLength }); - sendUpdateToPlayer(session, { - private: game.player - }); + } const discard = (game, session, discards) => { @@ -2515,283 +2515,272 @@ const discard = (game, session, discards) => { }); } -const asdf = () => { - const game = 0, session = 0; - switch (game) { - case 'select-resources': - if (!game || !game.turn || !game.turn.actions || - game.turn.actions.indexOf('select-resources') === -1) { - return `Please, let's not cheat. Ok?`; - console.log(game); - break; +const buyRoad = (game, session) => { + const player = session.player; + + if (game.state !== 'normal') { + return `You cannot purchase a road unless the game is active.`; + } + if (session.color !== game.turn.color) { + return `It is not your turn! It is ${game.turn.name}'s turn.`; + } + if (!game.turn.roll) { + return `You cannot build until you have rolled.`; + } + + if (game.turn && game.turn.robberInAction) { + return `Robber is in action. You can not purchase until all Robber tasks are resolved.`; + } + + if (player.brick < 1 || player.wood < 1) { + return `You have insufficient resources to build a road.`; + } + if (player.roads < 1) { + return `You have already built all of your roads.`; + } + const roads = getValidRoads(game, session.color); + if (roads.length === 0) { + return `There are no valid locations for you to place a road.`; + } + setForRoadPlacement(game, roads); + addActivity(game, session, `${game.turn.name} is considering building a road.`); + sendUpdateToPlayers(game, { + turn: game.turn, + chat: game.chat + }); +} + +const selectResources = (game, session, cards) => { + const player = session.player; + if (!game || !game.turn || !game.turn.actions || + game.turn.actions.indexOf('select-resources') === -1) { + return `Please, let's not cheat. Ok?`; + } + + if (session.color !== game.turn.color) { + return `It is not your turn! It is ${game.turn.name}'s turn.`; + } + + const count = (game.turn.active === 'monopoly') ? 1 : 2; + + if (!cards || cards.length > count || cards.length === 0) { + return `You have chosen the wrong number of cards!`; + } + + const isValidCard = (type) => { + switch (type.trim()) { + case 'wheat': + case 'brick': + case 'sheep': + case 'stone': + case 'wood': + return true; + default: + return false; + }; + } + + const selected = {}; + cards.forEach(card => { + if (!isValidCard(card)) { + return `Invalid resource type!`; } - - if (session.color !== game.turn.color) { - return `It is not your turn! It is ${game.turn.name}'s turn.`; - break; + if (card in selected) { + selected[card]++; + } else { + selected[card] = 1; } + }); + const display = []; + for (let card in selected) { + display.push(`${selected[card]} ${card}`); + } - const count = (game.turn.active === 'monopoly') ? 1 : 2; + addActivity(game, session, `${session.name} has chosen ${display.join(', ')}!`); - cards = req.body; - - if (!cards || cards.length > count || cards.length === 0) { - return `You have chosen the wrong number of cards!`; - break; - } - - const isValidCard = (type) => { - switch (type.trim()) { - case 'wheat': - case 'brick': - case 'sheep': - case 'stone': - case 'wood': - return true; - default: - return false; - }; - } - - const selected = {}; - cards.forEach(card => { - if (!isValidCard(card)) { - error = `Invalid resource type!`; + switch (game.turn.active) { + case 'monopoly': + const gave = [], type = cards[0]; + let total = 0; + for (let color in game.players) { + const player = game.players[color]; + if (player.status === 'Not active') { + continue } - if (card in selected) { - selected[card]++; - } else { - selected[card] = 1; + if (color === session.color) { + continue; } + if (player[type]) { + gave.push(`${player.name} gave ${player[type]} ${type}`); + session.player[type] += player[type]; + session.resources += player[type]; + total += player[type]; + player[type] = 0; + } + } + + if (gave.length) { + addChatMessage(game, session, `${session.name} player Monopoly and selected ${display.join(', ')}. ` + + `Players ${gave.join(', ')}. In total, they received ${total} ${type}.`); + } else { + addActivity(game, session, 'No players had that resource. Wa-waaaa.'); + } + break; + + case 'year-of-plenty': + cards.forEach(type => { + session.player[type]++; + session.player.resources++; }); - const display = []; - for (let card in selected) { - display.push(`${selected[card]} ${card}`); - } - - if (error) { - break; - } - - addActivity(game, session, `${session.name} has chosen ${display.join(', ')}!`); - - switch (game.turn.active) { - case 'monopoly': - const gave = [], type = cards[0]; - 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(`${player.name} gave ${player[type]} ${type}`); - session.player[type] += player[type]; - session.resources += player[type]; - total += player[type]; - player[type] = 0; - } - } - - if (gave.length) { - addChatMessage(game, session, `${session.name} player Monopoly and selected ${display.join(', ')}. ` + - `Players ${gave.join(', ')}. In total, they received ${total} ${type}.`); - } else { - addActivity(game, session, 'No players had that resource. Wa-waaaa.'); - } - break; - - case 'year-of-plenty': - cards.forEach(type => { - session.player[type]++; - session.player.resources++; - }); - addChatMessage(game, session, `${session.name} player Year of Plenty.` + - `They chose to receive ${display.join(', ')} 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.`; - 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 (game.turn && game.turn.robberInAction) { - error = `Robber is in action. You can not purchase until all Robber tasks are resolved.`; - break; - } - - 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; - } - corners = getValidCorners(game, session.color); - if (corners.length === 0) { - error = `There are no valid locations for you to place a settlement.`; - break; - } - setForSettlementPlacement(game, corners); - addActivity(game, session, `${game.turn.name} is considering placing a settlement.`); - 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 < 2 || player.stone < 3) { - error = `You have insufficient resources to build a city.`; - break; - } - - if (game.turn && game.turn.robberInAction) { - error = `Robber is in action. You can not purchase until all Robber tasks are resolved.`; - 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; - } - setForCityPlacement(game, corners); - addActivity(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 ${game.players[corner.color].name}!`; - break; - } - if (corner.type !== 'settlement') { - error = `This location already has a city!`; - break; - } - if (!game.turn.free) { - if (player.wheat < 2 || player.stone < 3) { - 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'; - debugChat(game, 'Before city purchase'); - - player.cities--; - player.settlements++; - if (!game.turn.free) { - addChatMessage(game, session, `${session.name} spent 2 wheat, 3 stone to upgrade to a city.`) - player.wheat -= 2; - player.stone -= 3; - player.resources = 0; - [ 'wheat', 'brick', 'sheep', 'stone', 'wood' ].forEach(resource => { - player.resources += player[resource]; - }); - } - delete game.turn.free; - - debugChat(game, 'After city purchase'); - game.turn.actions = []; - game.turn.limits = {}; - addActivity(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.`; - 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 (game.turn && game.turn.robberInAction) { - error = `Robber is in action. You can not purchase until all Robber tasks are resolved.`; - break; - } - - 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; - } - let roads = getValidRoads(game, session.color); - if (roads.length === 0) { - error = `There are no valid locations for you to place a road.`; - break; - } - setForRoadPlacement(game, roads); - addActivity(game, session, `${game.turn.name} is considering building a road.`); - break; - - break; - + addChatMessage(game, session, `${session.name} player Year of Plenty.` + + `They chose to receive ${display.join(', ')} from the bank.`); break; } -}; + delete game.turn.active; + game.turn.actions = []; + sendUpdateToPlayer(session, { + private: game.player + }); + sendUpdateToPlayers(game, { + turn: game.turn, + chat: game.chat, + activities: game.activities + }); +} + +const buySettlement = (game, session) => { + const player = session.player; + if (game.state !== 'normal') { + return `You cannot purchase a settlement unless the game is active.`; + } + if (session.color !== game.turn.color) { + return `It is not your turn! It is ${game.turn.name}'s turn.`; + } + if (!game.turn.roll) { + return `You cannot build until you have rolled.`; + } + + if (game.turn && game.turn.robberInAction) { + return `Robber is in action. You can not purchase until all Robber tasks are resolved.`; + } + + if (player.brick < 1 || player.wood < 1 || player.wheat < 1 || player.sheep < 1) { + return `You have insufficient resources to build a settlement.`; + } + if (player.settlements < 1) { + return `You have already built all of your settlements.`; + } + corners = getValidCorners(game, session.color); + if (corners.length === 0) { + return `There are no valid locations for you to place a settlement.`; + } + setForSettlementPlacement(game, corners); + addActivity(game, session, `${game.turn.name} is considering placing a settlement.`); + + sendUpdateToPlayers(game, { + turn: game.turn, + chat: game.chat, + activities: game.activities + }); +} + +const buyCity = (game, session) => { + const player = session.player; + if (game.state !== 'normal') { + return `You cannot purchase a city unless the game is active.`; + } + if (session.color !== game.turn.color) { + return `It is not your turn! It is ${game.turn.name}'s turn.`; + } + if (!game.turn.roll) { + return `You cannot build until you have rolled.`; + } + if (player.wheat < 2 || player.stone < 3) { + return `You have insufficient resources to build a city.`; + } + + if (game.turn && game.turn.robberInAction) { + return `Robber is in action. You can not purchase until all Robber tasks are resolved.`; + } + + if (player.city < 1) { + return `You have already built all of your cities.`; + } + corners = getValidCorners(game, session.color, 'settlement'); + if (corners.length === 0) { + return `There are no valid locations for you to place a city.`; + } + setForCityPlacement(game, corners); + addActivity(game, session, `${game.turn.name} is considering upgrading a settlement to a city.`); + sendUpdateToPlayers(game, { + turn: game.turn, + chat: game.chat, + activities: game.activities + }); +} + +const placeCity = (game, session, index) => { + const player = session.player; + index = parseInt(value); + if (game.state !== 'normal') { + return `You cannot place an item unless the game is active.`; + } + if (session.color !== game.turn.color) { + return `It is not your turn! It is ${game.turn.name}'s turn.`; + } + if (game.placements.corners[index] === undefined) { + return `You have requested to place a city illegally!`; + } + /* 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) { + return `You tried to cheat! You should not try to break the rules.`; + } + const corner = game.placements.corners[index]; + if (corner.color !== session.color) { + return `This location already has a settlement belonging to ${game.players[corner.color].name}!`; + } + if (corner.type !== 'settlement') { + return `This location already has a city!`; + } + if (!game.turn.free) { + if (player.wheat < 2 || player.stone < 3) { + return `You have insufficient resources to build a city.`; + } + } + if (player.city < 1) { + return `You have already built all of your cities.`; + } + + corner.color = session.color; + corner.type = 'city'; + debugChat(game, 'Before city purchase'); + + player.cities--; + player.settlements++; + if (!game.turn.free) { + addChatMessage(game, session, `${session.name} spent 2 wheat, 3 stone to upgrade to a city.`) + player.wheat -= 2; + player.stone -= 3; + player.resources = 0; + [ 'wheat', 'brick', 'sheep', 'stone', 'wood' ].forEach(resource => { + player.resources += player[resource]; + }); + } + delete game.turn.free; + + debugChat(game, 'After city purchase'); + game.turn.actions = []; + game.turn.limits = {}; + addActivity(game, session, `${session.name} upgraded a settlement to a city!`); + sendUpdateToPlayer(session, { + private: session.player + }); + sendUpdateToPlayers(game, { + turn: game.turn, + chat: game.chat, + activities: game.activities + }); +} const ping = (session) => { if (!session.ws) { @@ -2892,7 +2881,6 @@ const join = (peers, session, id) => { peers[session.name] = ws; }; - const part = (peers, session, id) => { const ws = session.ws; @@ -3036,7 +3024,6 @@ const sendUpdateToPlayers = async (game, update) => { await saveGame(game); } - const sendUpdateToPlayer = async (session, update) => { if (debug.update) { console.log(`${session.id}: -> sendUpdateToPlayer:${getName(session)} - `, update); @@ -3385,10 +3372,8 @@ const wsConnect = async (ws, req) => { case 'shuffle': console.log(`${short}: <- shuffle:${getName(session)}`); warning = shuffle(game, session); - if (error) { + if (warning) { warning(session, error); - } else { - warning(game); } break; case 'place-settlement': @@ -3398,6 +3383,13 @@ const wsConnect = async (ws, req) => { sendWarning(session, warning); } break; + case 'place-city': + console.log(`${short}: <- place-city:${getName(session)} ${data.index}`); + warning = placeCity(game, session, data.index); + if (warning) { + sendWarning(session, warning); + } + break; case 'place-road': console.log(`${short}: <- place-road:${getName(session)} ${data.index}`); warning = placeRoad(game, session, data.index); @@ -3433,6 +3425,65 @@ const wsConnect = async (ws, req) => { sendWarning(session, warning); } break; + case 'select-resources': + console.log(`${short}: <- select-resources:${getName(session)}`); + warning = selectResources(game, session, data.cards); + if (warning) { + sendWarning(session, warning); + } + break; + case 'buy-city': + console.log(`${short}: <- buy-city:${getName(session)}`); + warning = buyCity(game, session); + if (warning) { + sendWarning(session, warning); + } + break; + case 'buy-road': + console.log(`${short}: <- buy-road:${getName(session)}`); + warning = buyRoad(game, session); + if (warning) { + sendWarning(session, warning); + } + break; + case 'buy-settlement': + console.log(`${short}: <- buy-settlement:${getName(session)}`); + warning = buySettlement(game, session); + if (warning) { + sendWarning(session, warning); + } + break; + case 'buy-development': + console.log(`${short}: <- buy-development:${getName(session)}`); + warning = buyDevelopment(game, session); + if (warning) { + sendWarning(session, warning); + } + break; + case 'play-card': + console.log(`${short}: <- play-card:${getName(session)}`); + warning = playCard(game, session, data.card); + if (warning) { + sendWarning(session, warning); + } + break; + case 'trade': + console.log(`${short}: <- trade:${getName(session)}`); + warning = trade(game, session, data.action, data.offer); + if (warning) { + sendWarning(session, warning); + } else { + sendUpdateToPlayer(session, { + private: session.player + }); + sendUpdateToPlayers(game, { + players: getFilteredPlayers(game), + turn: game.turn, + activities: game.activities, + chat: game.chat + }); + } + break; default: console.warn(`Unsupported request: ${data.type}`); break;