diff --git a/server/roll b/server/roll index 4748466..51a54d5 100755 --- a/server/roll +++ b/server/roll @@ -6,10 +6,17 @@ if [[ "${ADMIN}" == "" ]]; then fi id=$1 -color=$2 +shift +color=$1 +shift +dice1=$1 +shift +if [[ "$1" != "" ]]; then + dice2=",$1" +fi -if [[ "${id}" == "" ]] || [[ "${color}" == "" ]]; then - echo "Usage: roll GAME-ID (red|white|blue|orange)-DICE" +if [[ "${id}" == "" ]] || [[ "${color}" == "" ]] || [[ "$dice1" == "" ]]; then + echo "Usage: roll GAME-ID (red|white|blue|orange) DICE [DICE]" exit 1 fi @@ -17,6 +24,6 @@ curl --noproxy '*' -s -L \ --request PUT \ --header "PRIVATE-TOKEN: ${ADMIN}" \ --header "Content-Type: application/json" \ - http://localhost:8930/ketr.ketran/api/v1/games/${id}/roll/${color} | - jq -r .status + http://localhost:8930/ketr.ketran/api/v1/games/${id}/roll/${color}?dice=${dice1}${dice2} +# | jq -r .status diff --git a/server/routes/games.js b/server/routes/games.js index cb144f2..5a2f12d 100755 --- a/server/routes/games.js +++ b/server/routes/games.js @@ -117,6 +117,7 @@ const processTies = (players) => { }); let ties = false, position = 1; + const irstify = (position) => { switch (position) { case 1: return `1st`; @@ -126,7 +127,7 @@ const processTies = (players) => { default: return position; } } -console.log(`Slots: `, slots); + /* Reverse from high to low */ slots.reverse().forEach((slot) => { if (slot.length !== 1) { @@ -217,22 +218,24 @@ const processGameOrder = (game, player, dice) => { }); } -const roll = (game, session) => { +const roll = (game, session, dice) => { const player = session.player, name = session.name ? session.name : "Unnamed"; + if (!dice) { + dice = [ Math.ceil(Math.random() * 6), Math.ceil(Math.random() * 6) ]; + } switch (game.state) { case "lobby": /* currently not available as roll is only after color is * set for players */ - addChatMessage(game, session, `${name} rolled ${Math.ceil(Math.random() * 6)}.`); + addChatMessage(game, session, `${name} rolled ${dice[0]}.`); sendUpdateToPlayers(game, { chat: game.chat }); return; case "game-order": game.startTime = Date.now(); - const dice = Math.ceil(Math.random() * 6); - addChatMessage(game, session, `${name} rolled ${dice}.`); - return processGameOrder(game, player, dice); + addChatMessage(game, session, `${name} rolled ${dice[0]}.`); + return processGameOrder(game, player, dice[0]); case "normal": if (game.turn.color !== session.color) { @@ -241,10 +244,21 @@ const roll = (game, session) => { if (game.turn.roll) { return `You already rolled this turn.`; } - processRoll(game, session, [ Math.ceil(Math.random() * 6), Math.ceil(Math.random() * 6) ]); + processRoll(game, session, dice); sendUpdateToPlayers(game, { chat: game.chat }); return; + case 'volcano-resource': + if (game.turn.color !== session.color) { + return `It is not your turn.`; + } + if (game.turn.volcano) { + return `You already rolled for the Volcano!`; + } + processVolcano(game, session, dice); + sendUpdateToPlayers(game, { chat: game.chat }); + return; + default: return `Invalid game state (${game.state}) in roll.`; } @@ -621,7 +635,7 @@ const canGiveBuilding = (game) => { } } -const adminActions = (game, action, value) => { +const adminActions = (game, action, value, query) => { let color, player, parts, session, corners, error; switch (action) { @@ -745,32 +759,47 @@ const adminActions = (game, action, value) => { return results; case "roll": - parts = value.match(/^([1-6])(-([1-6]))?$/); - if (!parts) { + let dice = (query.dice || '').split(','); + dice = dice.map(die => parseInt(die)); + + if (!value) { return `Unable to parse roll request.`; } - let dice = [ parseInt(parts[1]) ]; - if (parts[3]) { - dice.push(parseInt(parts[3])); + + switch (value) { + case 'orange': color = 'O'; break; + case 'red': color = 'R'; break; + case 'blue': color = 'B'; break; + case 'white': color = 'W'; break; } - for (let id in game.sessions) { - if (game.sessions[id].name === game.turn.name) { - session = game.sessions[id]; + if (!color) { + return `Unable to find player ${value}` + } + const rollingPlayer = (color) => { + for (let id in game.sessions) { + if ((color + && game.sessions[id].player + && game.sessions[id].player.color === color) + || (game.sessions[id].name === game.turn.name)) { + return game.sessions[id]; + } } + return undefined; + }; + + addChatMessage(game, null, + `Admin rolling ${dice.join(', ')} for ${value}.`); + if (game.state === 'game-order') { + session = rollingPlayer(color); + } else { + session = rollingPlayer(); } if (!session) { return `Unable to determine current player turn for admin roll.`; } - console.log(dice, parts); - addChatMessage(game, null, `Admin rolling ${dice.join(', ')} for ${game.turn.name}.`); - switch (game.state) { - case 'game-order': - addActivity(game, session, `${game.turn.name} rolled ${dice[0]}.`); - processGameOrder(game, session.player, dice[0]); - break; - case 'normal': - processRoll(game, session, dice); - break; + let warning = roll(game, session, dice); + if (warning) { + sendWarning(session, warning); } break; @@ -1401,9 +1430,14 @@ const getValidCorners = (game, color, type) => { * * 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 * check to see if there is a settlement placed at the end of that road + * * If so, this location cannot have a settlement. + * + * If still valid, and we are in initial settlement placement, and if + * Volcano is enabled, verify the tile is not the Volcano. */ layout.corners.forEach((corner, cornerIndex) => { const placement = game.placements.corners[cornerIndex]; @@ -1431,7 +1465,8 @@ const getValidCorners = (game, color, type) => { 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. */ + /* This side of the road is pointing to the corner being validated. + * Skip it. */ if (road.corners[c] === cornerIndex) { continue; } @@ -1443,7 +1478,11 @@ const getValidCorners = (game, color, type) => { } } if (valid) { - limits.push(cornerIndex); + if (game.state !== 'initial-placement' + || (isRuleEnabled(game, 'volcano') + && layout.tiles[game.robber].corners.indexOf(cornerIndex) === -1)) { + limits.push(cornerIndex); + } } }); @@ -1701,10 +1740,12 @@ router.put("/:id/:action/:value?", async (req, res) => { if (req.headers['private-token'] !== req.app.get('admin')) { error = `Invalid admin credentials.`; } else { - error = adminActions(game, action, value); + error = adminActions(game, action, value, req.query); } if (!error) { sendGameToPlayers(game); + } else { + console.log(`admin-action error: ${error}`); } }