Added more Robber processing
Signed-off-by: James Ketrenos <james_eikona@ketrenos.com>
This commit is contained in:
parent
e264f0ae8d
commit
37467c7377
@ -33,13 +33,15 @@
|
||||
background-size: 600% auto; /* pip-numbers is a 6x6 grid of pip images */
|
||||
width: 2em;
|
||||
height: 2em;
|
||||
transform: translate(-50%, -50%);
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
|
||||
.Pip.Active {
|
||||
filter: drop-shadow(0px 0px 5px rgba(255, 255, 0, 0.9));
|
||||
}
|
||||
|
||||
.Pips[disabled],
|
||||
.Tiles[disabled],
|
||||
.Roads[disabled],
|
||||
.Corners[disabled] {
|
||||
pointer-events: none;
|
||||
@ -107,19 +109,40 @@
|
||||
right: 0px;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
clip-path: circle(50%);
|
||||
/* clip-path: circle(50%);*/
|
||||
}
|
||||
|
||||
.Tile-Shape {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
top: 0px;
|
||||
bottom: 0px;
|
||||
clip-path: polygon(25% 0%,75% 0%,100% 50%,75% 100%,25% 100%,0% 50%);
|
||||
}
|
||||
|
||||
.Option {
|
||||
cursor: pointer;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.Option .Pip-Shape,
|
||||
.Option .Tile-Shape,
|
||||
.Option .Corner-Shape,
|
||||
.Option .Road-Shape {
|
||||
background-color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
|
||||
.Robber .Pip-Shape,
|
||||
.Pip-Shape:hover {
|
||||
clip-path: circle(45%); /* show through the border */
|
||||
background-size: contain;
|
||||
background-position: center;
|
||||
background-image:url("./assets/woman-robber.png");
|
||||
}
|
||||
|
||||
.Tile-Shape:hover,
|
||||
.Corner-Shape:hover,
|
||||
.Road-Shape:hover {
|
||||
background-color: white;
|
||||
|
@ -47,8 +47,8 @@ const Board = ({ table, game }) => {
|
||||
height: `${tileImageHeight}px`,
|
||||
backgroundImage: `url(${assetsPath}/gfx/tiles-${tile.type}.png)`,
|
||||
backgroundPositionY: `-${tile.card*tileHeight}px`
|
||||
}}
|
||||
/>;
|
||||
}}
|
||||
><div className="Tile-Shape"/></div>;
|
||||
};
|
||||
|
||||
const Road = ({road}) => {
|
||||
@ -85,6 +85,27 @@ const Board = ({ table, game }) => {
|
||||
><div className="Corner-Shape"/></div>;
|
||||
};
|
||||
|
||||
const Pip = ({pip}) => {
|
||||
const onClick = (event) => {
|
||||
console.log(`Pip ${pip.index}:`, game.layout.corners[pip.index]);
|
||||
table.placeRobber(pip.index);
|
||||
return;
|
||||
};
|
||||
|
||||
return <div className={`Pip${pip.robber ? ' Robber' : ''}`}
|
||||
onClick={onClick}
|
||||
data-roll={pip.roll}
|
||||
data-index={pip.index}
|
||||
style={{
|
||||
top: `${pip.top}px`,
|
||||
left: `${pip.left}px`,
|
||||
backgroundImage: `url(${assetsPath}/gfx/pip-numbers.png)`,
|
||||
backgroundPositionX: `${ 100. * (pip.order % 6) / 5.}%`,
|
||||
backgroundPositionY: `${ 100 * Math.floor(pip.order / 6) / 5. }%`
|
||||
}}
|
||||
><div className="Pip-Shape"/></div>;
|
||||
};
|
||||
|
||||
const generateRoads = () => {
|
||||
let row = 0, rowCount = 0;
|
||||
let y = -2.5 + tileHalfWidth - (rows.length - 1) * 0.5 * tileWidth,
|
||||
@ -229,20 +250,21 @@ const Board = ({ table, game }) => {
|
||||
let row = 0, rowCount = 0;
|
||||
let y = tileHalfWidth - (rows.length - 1) * 0.5 * tileWidth,
|
||||
x = -(rows[row] - 1) * 0.5 * tileHeight;
|
||||
let index = 0;
|
||||
let pip;
|
||||
return game.pipOrder.map(order => {
|
||||
const pip = game.pips[order];
|
||||
const div = <div
|
||||
data-roll={pip.roll}
|
||||
pip = {
|
||||
roll: game.pips[order].roll,
|
||||
robber: index === game.robber,
|
||||
index: index++,
|
||||
top: y,
|
||||
left: x,
|
||||
order: order
|
||||
};
|
||||
const div = <Pip
|
||||
pip={pip}
|
||||
key={`pip-${order}`}
|
||||
className="Pip"
|
||||
style={{
|
||||
top: `${y}px`,
|
||||
left: `${x}px`,
|
||||
backgroundImage: `url(${assetsPath}/gfx/pip-numbers.png)`,
|
||||
backgroundPositionX: `${ 100. * (order % 6) / 5.}%`,
|
||||
backgroundPositionY: `${ 100 * Math.floor(order / 6) / 5. }%`
|
||||
}}
|
||||
><div className="Pip-Shape"/></div>;
|
||||
/>;
|
||||
|
||||
if (++rowCount === rows[row]) {
|
||||
row++;
|
||||
@ -389,10 +411,11 @@ const Board = ({ table, game }) => {
|
||||
}
|
||||
});
|
||||
/* Clear all 'Option' targets */
|
||||
const nodes = document.querySelectorAll(`.Option`);
|
||||
let 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']) {
|
||||
@ -413,6 +436,38 @@ const Board = ({ table, game }) => {
|
||||
el.classList.add('Option');
|
||||
});
|
||||
}
|
||||
if (game.turn.limits['tiles']) {
|
||||
game.turn.limits['tiles'].forEach(index => {
|
||||
const el = document.querySelector(`.Tile[data-index="${index}"]`);
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
el.classList.add('Option');
|
||||
});
|
||||
}
|
||||
if (game.turn.limits['pips']) {
|
||||
game.turn.limits['pips'].forEach(index => {
|
||||
const el = document.querySelector(`.Pip[data-index="${index}"]`);
|
||||
if (!el) {
|
||||
return;
|
||||
}
|
||||
el.classList.add('Option');
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (game) {
|
||||
let nodes = document.querySelectorAll(`.Pip .Robber`);
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
nodes[i].classList.remove('Robber');
|
||||
}
|
||||
|
||||
if (game.robber) {
|
||||
const el = document.querySelector(`.Pip[data-index="${game.robber}"]`);
|
||||
if (el) {
|
||||
el.classList.add('Robber');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -424,9 +479,13 @@ const Board = ({ table, game }) => {
|
||||
<div className="Board">
|
||||
<div className="BoardBox">
|
||||
{ borders }
|
||||
{ tiles }
|
||||
{ pips }
|
||||
{ game && <>
|
||||
<div className="Tiles" disabled>
|
||||
{ tiles }
|
||||
</div>
|
||||
<div className="Pips" disabled={!canAction('place-robber') || game.turn.color !== game.color || (game.state !== 'initial-placement' && game.state !== 'normal')}>
|
||||
{ pips }
|
||||
</div>
|
||||
<div className="Corners" disabled={!canAction('place-settlement') || game.turn.color !== game.color || (game.state !== 'initial-placement' && game.state !== 'normal')}>
|
||||
{ corners }
|
||||
</div>
|
||||
|
@ -306,9 +306,15 @@
|
||||
}
|
||||
|
||||
.Hand {
|
||||
display: flex;
|
||||
min-height: calc(7.2em + 0.5em);
|
||||
}
|
||||
|
||||
.Hand > button {
|
||||
align-self: center;
|
||||
justify-self: center;
|
||||
}
|
||||
|
||||
.Hand:hover .Stack:hover > *:not(:first-child) {
|
||||
margin-left: -2em;
|
||||
}
|
||||
|
@ -136,6 +136,7 @@ const Resource = ({ type, count }) => {
|
||||
<div className="Stack">
|
||||
{ React.Children.map(array, i => (
|
||||
<div className="Resource"
|
||||
data-type={type}
|
||||
onClick={select}
|
||||
style={{backgroundImage:`url(${assetsPath}/gfx/card-${type}.png)`}}>
|
||||
</div>
|
||||
@ -336,6 +337,16 @@ const GameOrder = ({table}) => {
|
||||
};
|
||||
|
||||
const Action = ({ table }) => {
|
||||
const discardClick = (event) => {
|
||||
const nodes = document.querySelectorAll('.Hand .Selected'),
|
||||
discarding = { wheat: 0, brick: 0, sheep: 0, stone: 0, wood: 0 };
|
||||
for (let i = 0; i < nodes.length; i++) {
|
||||
discarding[nodes[i].getAttribute("data-type")]++;
|
||||
nodes[i].classList.remove('Selected');
|
||||
}
|
||||
return table.discard(discarding);
|
||||
}
|
||||
|
||||
const newTableClick = (event) => {
|
||||
return table.shuffleTable();
|
||||
};
|
||||
@ -357,7 +368,9 @@ const Action = ({ table }) => {
|
||||
return (<Paper className="Action"/>);
|
||||
}
|
||||
|
||||
const inLobby = table.game.state === 'lobby';
|
||||
const inLobby = table.game.state === 'lobby',
|
||||
player = table.game ? table.game.player : undefined;
|
||||
|
||||
return (
|
||||
<Paper className="Action">
|
||||
{ inLobby && <>
|
||||
@ -368,6 +381,9 @@ const Action = ({ table }) => {
|
||||
<Button disabled={(table.game.turn.color !== table.game.color || table.game.turn.roll) ? true : false} onClick={rollClick}>Roll Dice</Button>
|
||||
<Button disabled>Trade</Button>
|
||||
<Button disabled>Build</Button>
|
||||
{ table.game.turn.roll === 7 && player && player.mustDiscard > 0 &&
|
||||
<Button onClick={discardClick}>Discard</Button>
|
||||
}
|
||||
<Button disabled={(table.game.turn.color !== table.game.color || !table.game.turn.roll) ? true : false} onClick={passClick}>Done</Button>
|
||||
</> }
|
||||
{ !inLobby &&
|
||||
@ -493,6 +509,7 @@ class Table extends React.Component {
|
||||
this.rollDice = this.rollDice.bind(this);
|
||||
this.setGameState = this.setGameState.bind(this);
|
||||
this.shuffleTable = this.shuffleTable.bind(this);
|
||||
this.discard = this.discard.bind(this);
|
||||
this.passTurn = this.passTurn.bind(this);
|
||||
this.updateGame = this.updateGame.bind(this);
|
||||
this.setPlayerName = this.setPlayerName.bind(this);
|
||||
@ -577,6 +594,10 @@ class Table extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
discard(resources) {
|
||||
return this.sendAction('discard', undefined, resources);
|
||||
}
|
||||
|
||||
passTurn() {
|
||||
return this.sendAction('pass');
|
||||
};
|
||||
@ -662,6 +683,10 @@ class Table extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
placeRobber(robber) {
|
||||
return this.sendAction('place-robber', robber);
|
||||
};
|
||||
|
||||
placeSettlement(settlement) {
|
||||
return this.sendAction('place-settlement', settlement);
|
||||
}
|
||||
@ -813,6 +838,24 @@ class Table extends React.Component {
|
||||
case '':
|
||||
message = <>{message}The game is in a wonky state. Sorry :(</>;
|
||||
break;
|
||||
case 'normal':
|
||||
if (this.game && this.game.turn) {
|
||||
if (this.game.turn.roll === 7) {
|
||||
message = <>{message}Robber was rolled!</>;
|
||||
let move = true;
|
||||
for (let color in this.game.players) {
|
||||
const discard = this.game.players[color].mustDiscard;
|
||||
if (discard) {
|
||||
move = false;
|
||||
message = <>{message}<PlayerColor color={color}/> needs to discard {discard} resources.</>;
|
||||
}
|
||||
}
|
||||
if (move) {
|
||||
message = <>{message}<PlayerColor color={this.game.turn.color}/> {this.game.turn.name} needs to move the robber.</>
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
message = <>{message}Game state is: {this.game.state}</>;
|
||||
break;
|
||||
@ -909,15 +952,16 @@ class Table extends React.Component {
|
||||
<div className="Table">
|
||||
|
||||
<div style={{display: "inline-flex", flex: 1, flexDirection: "column"}}>
|
||||
<Board table={this} game={game}/>
|
||||
{ player !== undefined && <div className="Hand'">
|
||||
<Resource type="wood" count={player.wood}/>
|
||||
<Resource type="wheat" count={player.wheat}/>
|
||||
<Resource type="stone" count={player.stone}/>
|
||||
<Resource type="brick" count={player.brick}/>
|
||||
<Resource type="sheep" count={player.sheep}/>
|
||||
</div> }
|
||||
<Board table={this} game={game}/>
|
||||
{ player !== undefined && <div className="Hand">
|
||||
<Resource type="wood" count={player.wood}/>
|
||||
<Resource type="wheat" count={player.wheat}/>
|
||||
<Resource type="stone" count={player.stone}/>
|
||||
<Resource type="brick" count={player.brick}/>
|
||||
<Resource type="sheep" count={player.sheep}/>
|
||||
</div> }
|
||||
</div>
|
||||
|
||||
{ game && <div className={'Game ' + game.state}>
|
||||
<Paper className="Message">{ this.state.message }</Paper>
|
||||
{(this.state.pickName || !game.name) && <PlayerName table={this}/> }
|
||||
@ -934,7 +978,7 @@ class Table extends React.Component {
|
||||
|
||||
{ game && game.turn && game.turn.color !== game.color &&
|
||||
(game.state === 'initial-placement' || game.state === 'normal') &&
|
||||
<WaitingForPlayer table={this}/>
|
||||
(!game.player || !game.player.mustDiscard) && <WaitingForPlayer table={this}/>
|
||||
}
|
||||
|
||||
{ game && game.showCards &&
|
||||
|
BIN
client/src/assets/man-robber.png
Executable file
BIN
client/src/assets/man-robber.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 27 KiB |
BIN
client/src/assets/woman-robber.png
Executable file
BIN
client/src/assets/woman-robber.png
Executable file
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
@ -291,7 +291,11 @@ const distributeResources = (game, roll) => {
|
||||
for (let i = 0; i < game.pipOrder.length; i++) {
|
||||
let index = game.pipOrder[i];
|
||||
if (assetData.pips[index].roll === roll) {
|
||||
tiles.push(i);
|
||||
if (game.robber === i) {
|
||||
addChatMessage(game, null, `That pesky Robber stole resources!`);
|
||||
} else {
|
||||
tiles.push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -350,10 +354,40 @@ const processRoll = (game, dice) => {
|
||||
game.turn.roll = game.dice[0] + game.dice[1];
|
||||
if (game.turn.roll === 7) {
|
||||
addChatMessage(game, null, `ROBBER! ROBBER! ROBBER!`);
|
||||
for (let id in game.sessions) {
|
||||
const player = game.sessions[id].player;
|
||||
if (player) {
|
||||
let discard = player.stone + player.wheat + player.brick + player.wood + player.sheep;
|
||||
if (discard > 7) {
|
||||
discard = Math.floor(discard / 2);
|
||||
|
||||
player.mustDiscard = discard;
|
||||
addChatMessage(game, null, `${game.sessions[id].name} must discard ${discard} resource cards.`);
|
||||
} else {
|
||||
delete player.mustDiscard;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
if you roll a 7, no one receives any resource cards.
|
||||
instead, every player who has more than 7 resource cards must select
|
||||
half (rounded down) of their
|
||||
resource cards and return them to the bank.
|
||||
then you muyst move the robber:
|
||||
1. you must move the robber immediately to the number token of any other
|
||||
terrain ohex or to the desert hex,
|
||||
2. you then steal 1 (random) resourcde card from an opponent who has a settlement or city
|
||||
adjacent to the target terrain hex. the player who is robbed holds their resource cards
|
||||
face down. you then take 1 card at random. if the target hex is adjacent to 2 or
|
||||
more player's settlements or cities, you choose which one you want to steal from.
|
||||
If the production number for the hex containing the robber is rolled, the owners of
|
||||
adjacent settlements and citieis do not receive resourcres. The robber prevents it.
|
||||
*/
|
||||
} else {
|
||||
distributeResources(game, game.turn.roll);
|
||||
}
|
||||
}
|
||||
|
||||
const getPlayer = (game, color) => {
|
||||
if (!game) {
|
||||
return {
|
||||
@ -846,6 +880,34 @@ router.put("/:id/:action/:value?", async (req, res) => {
|
||||
addChatMessage(game, null, `It is ${next}'s turn.`);
|
||||
}
|
||||
break;
|
||||
case 'place-robber':
|
||||
if (game.state !== 'normal' && game.turn.roll !== 7) {
|
||||
error = `You cannot place robber unless 7 was rolled!`;
|
||||
break;
|
||||
}
|
||||
if (game.turn.name !== name) {
|
||||
error = `You cannot place the robber when it isn't your turn.`;
|
||||
break;
|
||||
}
|
||||
for (let color in game.players) {
|
||||
if (game.players[color].status === 'Not active') {
|
||||
continue;
|
||||
}
|
||||
if (game.players[color].mustDiscard > 0) {
|
||||
error = `You cannot place the robber until everyone has discarded!`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const robber = parseInt(value);
|
||||
if (game.robber === robber) {
|
||||
error = `You must move the robber to a new location!`;
|
||||
break;
|
||||
}
|
||||
game.robber = robber;
|
||||
game.turn.placedRobber = true;
|
||||
addChatMessage(game, session, `Robber has been moved!`);
|
||||
addChatMessage(game, null, 'TODO: Look up which players are on tile, then allow robber-roller to select which player to steal from.');
|
||||
break;
|
||||
case 'place-settlement':
|
||||
if (game.state !== 'initial-placement' && game.state !== 'normal') {
|
||||
error = `You cannot place an item unless the game is active.`;
|
||||
@ -979,6 +1041,33 @@ router.put("/:id/:action/:value?", async (req, res) => {
|
||||
case 'place-city':
|
||||
error = `City placement not yet implemented!`;
|
||||
break;
|
||||
case 'discard':
|
||||
if (game.turn.roll !== 7) {
|
||||
error = `You can only discard due to the Robber!`;
|
||||
break;
|
||||
}
|
||||
const discards = req.body, player = session.player;
|
||||
let sum = 0;
|
||||
for (let type in discards) {
|
||||
if (player[type] < parseInt(discards[type])) {
|
||||
error = `You have requested to discard more ${type} than you have.`
|
||||
break;
|
||||
}
|
||||
sum += parseInt(discards[type]);
|
||||
}
|
||||
if (sum > player.mustDiscard) {
|
||||
error = `You have requested to discard more cards than you are allowed!`;
|
||||
break;
|
||||
}
|
||||
for (let type in discards) {
|
||||
player[type] -= parseInt(discards[type]);
|
||||
player.mustDiscard -= parseInt(discards[type])
|
||||
}
|
||||
addChatMessage(game, null, `${session.name} discarded ${sum} resource cards.`);
|
||||
if (player.mustDiscard) {
|
||||
addChatMessage(game, null, `${session.name} must discard ${player.mustDiscard} more cards.`);
|
||||
}
|
||||
break;
|
||||
case "state":
|
||||
const state = value;
|
||||
if (!state) {
|
||||
@ -1048,6 +1137,28 @@ const sendGame = async (req, res, game, error) => {
|
||||
}
|
||||
game.active = active;
|
||||
|
||||
/* If the current turn is a robber placement, and everyone has
|
||||
* discarded, set the limits for where the robber can be placed */
|
||||
if (game.turn.roll === 7) {
|
||||
let move = true;
|
||||
for (let color in game.players) {
|
||||
const discard = game.players[color].mustDiscard;
|
||||
if (discard) {
|
||||
move = false;
|
||||
}
|
||||
}
|
||||
if (move && !game.turn.placedRobber) {
|
||||
game.turn.actions = [ 'place-robber' ];
|
||||
game.turn.limits = { pips: [] };
|
||||
for (let i = 0; i < 19; i++) {
|
||||
if (i === game.robber) {
|
||||
continue;
|
||||
}
|
||||
game.turn.limits.pips.push(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Update the session lastActive clock */
|
||||
let session;
|
||||
if (req.session) {
|
||||
@ -1117,6 +1228,14 @@ const resetGame = (game) => {
|
||||
roads: []
|
||||
};
|
||||
|
||||
for (let key in game.players) {
|
||||
game.players[key].wheat =
|
||||
game.players[key].sheep =
|
||||
game.players[key].stone =
|
||||
game.players[key].brick =
|
||||
game.players[key].wood = 0;
|
||||
}
|
||||
|
||||
for (let i = 0; i < layout.corners.length; i++) {
|
||||
game.placements.corners[i] = {
|
||||
color: undefined,
|
||||
@ -1247,6 +1366,7 @@ const shuffleBoard = (game) => {
|
||||
* the target pip value to the currently incremeneting
|
||||
* pip value. */
|
||||
if (game.tiles[game.tileOrder[target]].type === 'desert') {
|
||||
game.robber = target;
|
||||
game.pipOrder[target] = 18;
|
||||
} else {
|
||||
game.pipOrder[target] = p++;
|
||||
|
Loading…
x
Reference in New Issue
Block a user