Development cards can be purchased
Signed-off-by: James Ketrenos <james_eikona@ketrenos.com>
Before Width: | Height: | Size: 155 KiB After Width: | Height: | Size: 155 KiB |
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 160 KiB |
Before Width: | Height: | Size: 162 KiB After Width: | Height: | Size: 162 KiB |
BIN
client/public/assets/gfx/card-progress-year-of-plenty.png
Normal file
After Width: | Height: | Size: 142 KiB |
@ -71,7 +71,11 @@ const Board = ({ table, game }) => {
|
|||||||
const Corner = ({corner}) => {
|
const Corner = ({corner}) => {
|
||||||
const onClick = (event) => {
|
const onClick = (event) => {
|
||||||
console.log(`Corner ${corner.index}:`, game.layout.corners[corner.index]);
|
console.log(`Corner ${corner.index}:`, game.layout.corners[corner.index]);
|
||||||
|
if (event.currentTarget.getAttribute('data-type') === 'settlement') {
|
||||||
|
table.placeCity(corner.index);
|
||||||
|
} else {
|
||||||
table.placeSettlement(corner.index);
|
table.placeSettlement(corner.index);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -115,10 +115,12 @@ const Placard = ({table, type, active}) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const cityClicked = (event) => {
|
const cityClicked = (event) => {
|
||||||
|
table.buyCity();
|
||||||
table.setState({ buildActive: false });
|
table.setState({ buildActive: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
const developmentClicked = (event) => {
|
const developmentClicked = (event) => {
|
||||||
|
table.buyDevelopment();
|
||||||
table.setState({ buildActive: false });
|
table.setState({ buildActive: false });
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -153,26 +155,13 @@ const Placard = ({table, type, active}) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
class Development extends React.Component {
|
const Development = ({table, type}) => {
|
||||||
render() {
|
|
||||||
const array = [];
|
|
||||||
for (let i = 0; i < this.props.count; i++) {
|
|
||||||
if (this.props.type.match(/-$/)) {
|
|
||||||
array.push(i + 1);//Math.ceil(Math.random() * this.props.max));
|
|
||||||
} else {
|
|
||||||
array.push("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return (
|
return (
|
||||||
<div className="Stack">
|
|
||||||
{ React.Children.map(array, i => (
|
|
||||||
<div className="Development"
|
<div className="Development"
|
||||||
style={{backgroundImage:`url(${assetsPath}/gfx/card-${this.props.type}${i}.png)`}}>
|
style={{
|
||||||
</div>
|
backgroundImage:`url(${assetsPath}/gfx/card-${type}.png)`
|
||||||
)) }
|
}}/>
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const Resource = ({ type, count }) => {
|
const Resource = ({ type, count }) => {
|
||||||
@ -466,14 +455,14 @@ const Action = ({ table }) => {
|
|||||||
|
|
||||||
const inLobby = table.game.state === 'lobby',
|
const inLobby = table.game.state === 'lobby',
|
||||||
player = table.game ? table.game.player : undefined,
|
player = table.game ? table.game.player : undefined,
|
||||||
hasRolled = table.game && table.game.turn && table.game.turn.roll,
|
hasRolled = (table.game && table.game.turn && table.game.turn.roll) ? true : false,
|
||||||
isTurn = table.game && table.game.turn && table.game.turn.color === table.game.color;
|
isTurn = (table.game && table.game.turn && table.game.turn.color === table.game.color) ? true : false;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper className="Action">
|
<Paper className="Action">
|
||||||
{ inLobby && <>
|
{ inLobby && <>
|
||||||
<StartButton table={table}/>
|
<StartButton table={table}/>
|
||||||
<Button disabled={!table.game.color} onClick={newTableClick}>New table</Button>
|
<Button disabled={table.game.color ? false : true} onClick={newTableClick}>New table</Button>
|
||||||
<Button disabled={table.game.color ? true : false} onClick={() => {table.setState({ pickName: true})}}>Change name</Button> </> }
|
<Button disabled={table.game.color ? true : false} onClick={() => {table.setState({ pickName: true})}}>Change name</Button> </> }
|
||||||
{ table.game.state === 'normal' && <>
|
{ table.game.state === 'normal' && <>
|
||||||
<Button disabled={!isTurn || hasRolled} onClick={rollClick}>Roll Dice</Button>
|
<Button disabled={!isTurn || hasRolled} onClick={rollClick}>Roll Dice</Button>
|
||||||
@ -792,13 +781,25 @@ class Table extends React.Component {
|
|||||||
return this.sendAction('place-robber', robber);
|
return this.sendAction('place-robber', robber);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
buyDevelopment() {
|
||||||
|
return this.sendAction('buy-development');
|
||||||
|
}
|
||||||
|
|
||||||
buySettlement() {
|
buySettlement() {
|
||||||
return this.sendAction('buy-settlement');
|
return this.sendAction('buy-settlement');
|
||||||
}
|
}
|
||||||
|
|
||||||
placeSettlement(settlement) {
|
placeSettlement(settlement) {
|
||||||
return this.sendAction('place-settlement', settlement);
|
return this.sendAction('place-settlement', settlement);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
buyCity() {
|
||||||
|
return this.sendAction('buy-city');
|
||||||
|
}
|
||||||
|
placeCity(city) {
|
||||||
|
return this.sendAction('place-city', city);
|
||||||
|
}
|
||||||
|
|
||||||
buyRoad() {
|
buyRoad() {
|
||||||
return this.sendAction('buy-road');
|
return this.sendAction('buy-road');
|
||||||
}
|
}
|
||||||
@ -812,49 +813,8 @@ class Table extends React.Component {
|
|||||||
|
|
||||||
throwDice() {
|
throwDice() {
|
||||||
return this.rollDice();
|
return this.rollDice();
|
||||||
|
|
||||||
if (0) {
|
|
||||||
if (this.game.state !== 'active') {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const sum = 0;//dice[0].pips + dice[1].pips;
|
|
||||||
if (sum === 7) { /* Robber! */
|
|
||||||
if (this.state.total > 7) {
|
|
||||||
let half = Math.ceil(this.state.total * 0.5);
|
|
||||||
this.setState({ total: this.state.total - half});
|
|
||||||
while (half) {
|
|
||||||
switch (Math.floor(Math.random() * 5)) {
|
|
||||||
case 0: if (this.state.wood) { this.setState({ wood: this.state.wood - 1}); half--; } break;
|
|
||||||
case 1: if (this.state.sheep) { this.setState({ sheep: this.state.sheep - 1}); half--; } break;
|
|
||||||
case 2: if (this.state.stone) { this.setState({ stone: this.state.stone - 1}); half--; } break;
|
|
||||||
case 3: if (this.state.brick) { this.setState({ brick: this.state.brick - 1}); half--; } break;
|
|
||||||
case 4:
|
|
||||||
default: if (this.state.wheat) { this.setState({ wheat: this.state.wheat - 1}); half--; } break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.tiles.forEach((tile) => {
|
|
||||||
if (tile.pip.roll !== sum) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.setState({ [tile.type]: this.state[tile.type] + 1});
|
|
||||||
this.setState({ total: this.state.total + 1 });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
total: this.state.total,
|
|
||||||
wood: this.state.wood,
|
|
||||||
sheep: this.state.sheep,
|
|
||||||
stone: this.state.stone,
|
|
||||||
brick: this.state.brick,
|
|
||||||
wheat: this.state.wheat
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
updateDimensions() {
|
updateDimensions() {
|
||||||
const hasToolbar = false;
|
const hasToolbar = false;
|
||||||
|
|
||||||
@ -937,15 +897,12 @@ class Table extends React.Component {
|
|||||||
message = <>{message}You need to roll for game order. Click <b>Roll Dice</b> below.</>;
|
message = <>{message}You need to roll for game order. Click <b>Roll Dice</b> below.</>;
|
||||||
} else {
|
} else {
|
||||||
message = <>{message}You rolled <Dice pips={player.order}/> for game order. Waiting for all players to roll.</>;
|
message = <>{message}You rolled <Dice pips={player.order}/> for game order. Waiting for all players to roll.</>;
|
||||||
message = <>{message}<br/><b>THIS IS THE END OF THE FUNCTIONALITY SO FAR</b></>;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'active':
|
case 'active':
|
||||||
if (!player) {
|
if (!player) {
|
||||||
message = <>{message}This game is no longer in the lobby.<br/><b>TODO: Override game state to allow Lobby mode while in-game</b></>;
|
message = <>{message}This game is no longer in the lobby.<br/><b>TODO: Override game state to allow Lobby mode while in-game</b></>;
|
||||||
} else {
|
|
||||||
message = <>{message}<br/><b>THIS IS THE END OF THE FUNCTIONALITY SO FAR</b></>;
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case null:
|
case null:
|
||||||
@ -974,6 +931,8 @@ class Table extends React.Component {
|
|||||||
if (move && (this.game.turn && !this.game.turn.placedRobber)) {
|
if (move && (this.game.turn && !this.game.turn.placedRobber)) {
|
||||||
message = <>{message}<PlayerColor color={this.game.turn.color}/> {this.game.turn.name} needs to move the robber.</>
|
message = <>{message}<PlayerColor color={this.game.turn.color}/> {this.game.turn.name} needs to move the robber.</>
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
message = <>It is <PlayerColor color={this.game.turn.color}/> {this.game.turn.name}'s turn.</>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -1075,6 +1034,20 @@ class Table extends React.Component {
|
|||||||
case "B": color = "blue"; break;
|
case "B": color = "blue"; break;
|
||||||
case "W": color = "white"; break;
|
case "W": color = "white"; break;
|
||||||
}
|
}
|
||||||
|
let development;
|
||||||
|
if (player) {
|
||||||
|
let stacks = {};
|
||||||
|
game.player.development.forEach(item => (item.type in stacks) ? stacks[item.type].push(item.card) : stacks[item.type] = [item.card]);
|
||||||
|
console.log(stacks);
|
||||||
|
development = [];
|
||||||
|
for (let type in stacks) {
|
||||||
|
console.log(type, stacks[type]);
|
||||||
|
const cards = stacks[type].map(card => <Development table={this} type={`${type}-${card}`}/>);
|
||||||
|
development.push(<div key={type} className="Stack">{ cards }</div>);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
development = <>/</>;
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className="Table">
|
<div className="Table">
|
||||||
|
|
||||||
@ -1089,6 +1062,9 @@ class Table extends React.Component {
|
|||||||
<Resource type="brick" count={player.brick}/>
|
<Resource type="brick" count={player.brick}/>
|
||||||
<Resource type="sheep" count={player.sheep}/>
|
<Resource type="sheep" count={player.sheep}/>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="Hand">
|
||||||
|
{ development }
|
||||||
|
</div>
|
||||||
<Placard
|
<Placard
|
||||||
active={this.state.buildActive}
|
active={this.state.buildActive}
|
||||||
disabled={!game || !game.turn || !game.turn.roll}
|
disabled={!game || !game.turn || !game.turn.roll}
|
||||||
|
22
server/give
Executable file
@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
ADMIN=$(jq -r .admin config/local.json)
|
||||||
|
if [[ "${ADMIN}" == "" ]]; then
|
||||||
|
echo "You need to set your { 'admin': 'secret' } in config/local.json"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
id=$1
|
||||||
|
give=$2
|
||||||
|
|
||||||
|
if [[ "${id}" == "" ]] || [[ "${give}" == "" ]]; then
|
||||||
|
echo "Usage: give GAME-ID (wheat|wood|stone|brick|sheep)-COUNT"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
curl --noproxy '*' -s -L \
|
||||||
|
--request PUT \
|
||||||
|
--header "PRIVATE-TOKEN: ${ADMIN}" \
|
||||||
|
--header "Content-Type: application/json" \
|
||||||
|
http://localhost:8930/ketr.ketran/api/v1/games/${id}/give/${give} |
|
||||||
|
jq -r .status
|
||||||
|
|
@ -88,18 +88,26 @@ const assetData = {
|
|||||||
{ left: "sheep", right: "bank" },
|
{ left: "sheep", right: "bank" },
|
||||||
{ center: "bank" }
|
{ center: "bank" }
|
||||||
],
|
],
|
||||||
developmentCards: []
|
developmentCards: [
|
||||||
|
]
|
||||||
};
|
};
|
||||||
for (let i = 0; i < 14; i++) {
|
for (let i = 1; i <= 14; i++) {
|
||||||
assetData.developmentCards.push("knight");
|
assetData.developmentCards.push({
|
||||||
}
|
type: 'army',
|
||||||
for (let i = 0; i < 6; i++) {
|
card: i
|
||||||
assetData.developmentCards.push("progress");
|
});
|
||||||
}
|
|
||||||
for (let i = 0; i < 5; i++) {
|
|
||||||
assetData.developmentCards.push("victoryPoint");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[ 'monopoly', 'road-1', 'road-2', 'yeard-of-plenty'].forEach(card => assetData.developmentCards.push({
|
||||||
|
type: 'progress',
|
||||||
|
card: card
|
||||||
|
}));
|
||||||
|
|
||||||
|
[ 'market', 'library', 'palace', 'university'].forEach(card => assetData.developmentCards.push({
|
||||||
|
type: 'vp',
|
||||||
|
card: card
|
||||||
|
}));
|
||||||
|
|
||||||
const games = {};
|
const games = {};
|
||||||
|
|
||||||
const processTies = (players) => {
|
const processTies = (players) => {
|
||||||
@ -405,7 +413,8 @@ const getPlayer = (game, color) => {
|
|||||||
wheat: 0,
|
wheat: 0,
|
||||||
sheep: 0,
|
sheep: 0,
|
||||||
wood: 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;
|
games[id] = game;
|
||||||
return game;
|
return game;
|
||||||
};
|
};
|
||||||
@ -790,10 +805,13 @@ const getPrevPlayer = (game, name) => {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getValidCorners = (game, color) => {
|
const getValidCorners = (game, color, type) => {
|
||||||
const limits = [];
|
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 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 it connects to a road that is owned by that player.
|
||||||
* If no color is set, walk each road that leaves that corner and
|
* 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.
|
* If so, this location cannot have a settlement.
|
||||||
*/
|
*/
|
||||||
layout.corners.forEach((corner, cornerIndex) => {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (placement.color) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let valid;
|
let valid;
|
||||||
if (!color) {
|
if (!color) {
|
||||||
valid = true; /* Not filtering based on current player */
|
valid = true; /* Not filtering based on current player */
|
||||||
@ -918,10 +945,13 @@ router.put("/:id/:action/:value?", async (req, res) => {
|
|||||||
const name = session.name;
|
const name = session.name;
|
||||||
let message, index;
|
let message, index;
|
||||||
|
|
||||||
|
let corners, corner;
|
||||||
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case "roll":
|
case "roll":
|
||||||
error = roll(game, session);
|
error = roll(game, session);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "shuffle":
|
case "shuffle":
|
||||||
if (game.state !== "lobby") {
|
if (game.state !== "lobby") {
|
||||||
error = `Game no longer in lobby (${game.state}). Can not shuffle board.`;
|
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);
|
console.log(message);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'pass':
|
case 'pass':
|
||||||
if (game.turn.name !== name) {
|
if (game.turn.name !== name) {
|
||||||
error = `You cannot pass when it isn't your turn.`
|
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, session, `${name} passed their turn.`);
|
||||||
addChatMessage(game, null, `It is ${next}'s turn.`);
|
addChatMessage(game, null, `It is ${next}'s turn.`);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'place-robber':
|
case 'place-robber':
|
||||||
if (game.state !== 'normal' && game.turn.roll !== 7) {
|
if (game.state !== 'normal' && game.turn.roll !== 7) {
|
||||||
error = `You cannot place robber unless 7 was rolled!`;
|
error = `You cannot place robber unless 7 was rolled!`;
|
||||||
@ -1004,6 +1036,7 @@ router.put("/:id/:action/:value?", async (req, res) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'steal-resource':
|
case 'steal-resource':
|
||||||
if (game.turn.actions.indexOf('steal-resource') === -1) {
|
if (game.turn.actions.indexOf('steal-resource') === -1) {
|
||||||
error = `You can only steal a resource when it is valid to do so!`;
|
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;
|
game.turn.robberDone = true;
|
||||||
break;
|
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':
|
case 'buy-settlement':
|
||||||
if (game.state !== 'normal') {
|
if (game.state !== 'normal') {
|
||||||
error = `You cannot purchase a settlement unless the game is active.`;
|
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.`;
|
error = `It is not your turn! It is ${game.turn.name}'s turn.`;
|
||||||
break;
|
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) {
|
if (player.brick < 1 || player.wood < 1 || player.wheat < 1 || player.sheep < 1) {
|
||||||
error = `You have insufficient resources to build a settlement.`;
|
error = `You have insufficient resources to build a settlement.`;
|
||||||
break;
|
break;
|
||||||
@ -1051,20 +1120,16 @@ router.put("/:id/:action/:value?", async (req, res) => {
|
|||||||
error = `You have already built all of your settlements.`;
|
error = `You have already built all of your settlements.`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
let corners = getValidCorners(game, session.color);
|
corners = getValidCorners(game, session.color);
|
||||||
if (corners.length === 0) {
|
if (corners.length === 0) {
|
||||||
error = `There are no valid locations for you to place a settlement.`;
|
error = `There are no valid locations for you to place a settlement.`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
player.settlements--;
|
|
||||||
player.brick--;
|
|
||||||
player.wood--;
|
|
||||||
player.wheat--;
|
|
||||||
player.sheep--;
|
|
||||||
game.turn.actions = ['place-settlement'];
|
game.turn.actions = ['place-settlement'];
|
||||||
game.turn.limits = { corners };
|
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;
|
break;
|
||||||
|
|
||||||
case 'place-settlement':
|
case 'place-settlement':
|
||||||
if (game.state !== 'initial-placement' && game.state !== 'normal') {
|
if (game.state !== 'initial-placement' && game.state !== 'normal') {
|
||||||
error = `You cannot place an item unless the game is active.`;
|
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.`;
|
error = `You tried to cheat! You should not try to break the rules.`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const corner = game.placements.corners[index];
|
corner = game.placements.corners[index];
|
||||||
if (corner.color) {
|
if (corner.color) {
|
||||||
error = `This location already has a settlement belonging to ${playerNameFromColor(game, corner.color)}!`;
|
error = `This location already has a settlement belonging to ${playerNameFromColor(game, corner.color)}!`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
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.color = session.color;
|
||||||
corner.type = 'settlement';
|
corner.type = 'settlement';
|
||||||
if (game.state === 'normal') {
|
|
||||||
game.turn.actions = [];
|
game.turn.actions = [];
|
||||||
game.turn.limits = {};
|
game.turn.limits = {};
|
||||||
addChatMessage(game, session, `${name} placed a settlement.`);
|
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') {
|
if (game.direction && game.direction === 'backward') {
|
||||||
session.initialSettlement = index;
|
session.initialSettlement = index;
|
||||||
}
|
}
|
||||||
|
corner.color = session.color;
|
||||||
|
corner.type = 'settlement';
|
||||||
game.turn.actions = ['place-road'];
|
game.turn.actions = ['place-road'];
|
||||||
game.turn.limits = { roads: layout.corners[index].roads }; /* road placement is limited to be near this corner */
|
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.`);
|
addChatMessage(game, session, `Placed a settlement. Next, they need to place a road.`);
|
||||||
}
|
}
|
||||||
break;
|
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':
|
case 'buy-road':
|
||||||
if (game.state !== 'normal') {
|
if (game.state !== 'normal') {
|
||||||
error = `You cannot purchase a road unless the game is active.`;
|
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.`;
|
error = `It is not your turn! It is ${game.turn.name}'s turn.`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (!game.turn.roll) {
|
||||||
|
error = `You cannot build until you have rolled.`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (player.brick < 1 || player.wood < 1) {
|
if (player.brick < 1 || player.wood < 1) {
|
||||||
error = `You have insufficient resources to build a road.`;
|
error = `You have insufficient resources to build a road.`;
|
||||||
break;
|
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.`;
|
error = `There are no valid locations for you to place a road.`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
player.roads--;
|
|
||||||
player.brick--;
|
|
||||||
player.wood--;
|
|
||||||
game.turn.actions = ['place-road'];
|
game.turn.actions = ['place-road'];
|
||||||
game.turn.limits = { roads };
|
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;
|
break;
|
||||||
|
|
||||||
case 'place-road':
|
case 'place-road':
|
||||||
if (game.state !== 'initial-placement' && game.state !== 'normal') {
|
if (game.state !== 'initial-placement' && game.state !== 'normal') {
|
||||||
error = `You cannot place an item unless the game is active.`;
|
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)}!`;
|
error = `This location already has a road belonging to ${playerNameFromColor(game, road.color)}!`;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
road.color = session.color;
|
|
||||||
|
|
||||||
if (game.state === 'normal') {
|
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.actions = [];
|
||||||
game.turn.limits = {};
|
game.turn.limits = {};
|
||||||
addChatMessage(game, session, `${name} placed a road.`);
|
addChatMessage(game, session, `${name} placed a road.`);
|
||||||
} else if (game.state === 'initial-placement') {
|
} else if (game.state === 'initial-placement') {
|
||||||
|
road.color = session.color;
|
||||||
addChatMessage(game, session, `${name} placed a road.`);
|
addChatMessage(game, session, `${name} placed a road.`);
|
||||||
|
|
||||||
let next;
|
let next;
|
||||||
@ -1228,9 +1401,7 @@ router.put("/:id/:action/:value?", async (req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'place-city':
|
|
||||||
error = `City placement not yet implemented!`;
|
|
||||||
break;
|
|
||||||
case 'discard':
|
case 'discard':
|
||||||
if (game.turn.roll !== 7) {
|
if (game.turn.roll !== 7) {
|
||||||
error = `You can only discard due to the Robber!`;
|
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.`);
|
addChatMessage(game, null, `${session.name} must discard ${player.mustDiscard} more cards.`);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "state":
|
case "state":
|
||||||
const state = value;
|
const state = value;
|
||||||
if (!state) {
|
if (!state) {
|
||||||
@ -1568,7 +1740,7 @@ const shuffleBoard = (game) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
shuffle(game.developmentCards)
|
shuffle(game.developmentCards);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|