diff --git a/client/src/Board.css b/client/src/Board.css index dbce636..59a9d19 100644 --- a/client/src/Board.css +++ b/client/src/Board.css @@ -54,6 +54,7 @@ height: 1.5em; transform: translate(-50%, -50%); z-index: 5; + pointer-events: none; } .Corner-Shape { @@ -66,14 +67,6 @@ clip-path: polygon(50% 0%,70% 15%,70% 2%,90% 2%,90% 30%,100% 40%,100% 100%,65% 100%,65% 65%,35% 65%,35% 100%,0% 100%,0% 40%); } -.Corner .Option { - background-color: rgba(255, 255, 255, 0.5); -} - -.Corner-Shape:hover { - background-color: white; -} - .Road { position: absolute; display: flex; @@ -84,6 +77,7 @@ width: 0.5em; height: 2.5em; z-index: 0; + pointer-events: none; } .Road-Shape { @@ -97,10 +91,16 @@ } -.Road .Option { +.Option { + pointer-events: all; +} + +.Option .Corner-Shape, +.Option .Road-Shape { background-color: rgba(255, 255, 255, 0.5); } +.Corner-Shape:hover, .Road-Shape:hover { background-color: white; } @@ -112,12 +112,12 @@ [data-color='R'] > .Corner-Shape, [data-color='R'] > .Road-Shape { - background-color: rgba(255, 0, 0, 1); + background-color: rgba(255, 0, 0, 1); } [data-color='O'] > .Corner-Shape, [data-color='O'] > .Road-Shape { - background-color: rgba(255, 196, 0, 1); + background-color: rgba(255, 196, 0, 1); } [data-color='W'] > .Corner-Shape, @@ -127,5 +127,5 @@ [data-color='B'] > .Corner-Shape, [data-color='B'] > .Road-Shape { - background-color: rgba(0, 0, 255, 1); + background-color: rgba(0, 0, 255, 1); } \ No newline at end of file diff --git a/client/src/Board.js b/client/src/Board.js index 26954cc..4c72143 100644 --- a/client/src/Board.js +++ b/client/src/Board.js @@ -352,6 +352,7 @@ const Board = ({ table, game }) => { } if (game && game.placements) { + /* Set color and type based on placement data from the server */ game.placements.corners.forEach((corner, index) => { const el = document.querySelector(`.Corner[data-index="${index}"]`); if (!el) { @@ -376,6 +377,32 @@ const Board = ({ table, game }) => { el.setAttribute('data-color', road.color); } }); + /* Clear all 'Option' targets */ + const nodes = document.querySelectorAll(`.Option`); + for (let i = 0; i < nodes.length; i++) { + nodes[i].classList.remove('Option'); + } + /* Add 'Option' based on game.turn.limits */ + if (game.turn && game.turn.limits) { + if (game.turn.limits['roads']) { + game.turn.limits['roads'].forEach(index => { + const el = document.querySelector(`.Road[data-index="${index}"]`); + if (!el) { + return; + } + el.classList.add('Option'); + }); + } + if (game.turn.limits['corners']) { + game.turn.limits['corners'].forEach(index => { + const el = document.querySelector(`.Corner[data-index="${index}"]`); + if (!el) { + return; + } + el.classList.add('Option'); + }); + } + } } const canAction = (action) => { diff --git a/server/routes/games.js b/server/routes/games.js index 9b31f28..3320811 100755 --- a/server/routes/games.js +++ b/server/routes/games.js @@ -205,8 +205,10 @@ const processGameOrder = (game, player, dice) => { game.playerOrder = players.map(player => getPlayerColor(game, player)); game.state = 'initial-placement'; message = `Initial settlement placement has started!`; + game.turn = { actions: ['place-settlement'], + limits: { corners: getValidCorners(game) }, name: getPlayerName(game, players[0]), color: getPlayerColor(game, players[0]) }; @@ -583,6 +585,41 @@ const getNextPlayer = (game, name) => { return name; } +const getValidCorners = (game) => { + const limits = []; + + /* For each corner, if the corner already has a color set, skip it + * If no color is set, walk each road that leaves that corner and + * check to see if there is a settlement placed at the end of that road + * If so, this location cannot have a settlement. + */ + layout.corners.forEach((corner, cornerIndex) => { + if (game.placements.corners[cornerIndex].color) { + return; + } + let valid = true; + for (let r = 0; valid && r < corner.roads.length; r++) { + const road = layout.roads[corner.roads[r]]; + for (let c = 0; valid && c < road.corners.length; c++) { + /* This side of the road is pointing to the corner being validated. Skip it. */ + if (road.corners[c] === cornerIndex) { + continue; + } + /* There is a settlement within one segment from this + * corner, so it is invalid for settlement placement */ + if (game.placements.corners[road.corners[c]].color) { + valid = false; + } + } + } + if (valid) { + limits.push(cornerIndex); + } + }); + + return limits; +} + router.put("/:id/:action/:value?", async (req, res) => { const { action, id } = req.params, value = req.params.value ? req.params.value : ""; @@ -683,7 +720,7 @@ router.put("/:id/:action/:value?", async (req, res) => { 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 */ + 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.`); } else { error = `Settlement placement not enabled for normal game play.`; @@ -715,6 +752,7 @@ router.put("/:id/:action/:value?", async (req, res) => { const next = getNextPlayer(game, name); game.turn = { actions: ['place-settlement'], + limits: { corners: getValidCorners(game) }, name: next, color: getColorFromName(game, next) };