1
0

Use "resources" instead of "haveResources"

Updated client to use private instead of player

Signed-off-by: James Ketrenos <james_eikona@ketrenos.com>
This commit is contained in:
James Ketrenos 2022-03-12 15:30:03 -08:00
parent 27f143c6a0
commit dac755cad0
6 changed files with 225 additions and 256 deletions

View File

@ -1,52 +1,42 @@
import React, { useState, useEffect, useContext, useRef, useMemo } from "react";
import "./Actions.css";
import Paper from '@material-ui/core/Paper';
import Button from '@material-ui/core/Button';
import { PlayerName } from './PlayerName.js';
import equal from "fast-deep-equal";
import "./Actions.css";
import { PlayerName } from './PlayerName.js';
import { GlobalContext } from "./GlobalContext.js";
const Actions = () => {
const { ws, gameId, name } = useContext(GlobalContext);
const [state, setState] = useState('lobby');
const [color, setColor] = useState(undefined);
const [player, setPlayer] = useState(undefined);
const [players, setPlayers] = useState(undefined);
const [priv, setPriv] = useState(undefined);
const [turn, setTurn] = useState({});
const [active, setActive] = useState(0);
const [edit, setEdit] = useState(name);
const fields = useMemo(() => [
'state', 'turn', 'player', 'active', 'color'
'state', 'turn', 'private', 'active', 'color'
], []);
const onWsMessage = (event) => {
const data = JSON.parse(event.data);
switch (data.type) {
case 'game-update':
if ('private' in data.update && !equal(data.update.private, priv)) {
setPriv(data.update.private);
}
if ('state' in data.update && data.update.state !== state) {
setState(data.update.state);
}
if ('color' in data.update && data.update.color !== color) {
setColor(data.update.color);
if (players && color in players) {
setPlayer(players[color]);
} else if (player) {
setPlayer(undefined);
}
}
if ('name' in data.update && data.update.name !== edit) {
setEdit(data.update.name);
}
if ('players' in data.update) {
setPlayers(data.update.players);
if (color in data.update.players) {
setPlayer(data.update.players[color]);
} else if (player) {
setPlayer(undefined);
}
}
if ('turn' in data.update) {
if ('turn' in data.update && !equal(data.update.turn, turn)) {
setTurn(data.update.turn);
}
if ('active' in data.update && data.update.active !== active) {
@ -143,11 +133,11 @@ const Actions = () => {
const inLobby = state === 'lobby',
inGame = state === 'normal',
inGameOrder = state === 'game-order',
hasGameOrderRolled = (player && player.orderRoll) ? true : false,
hasGameOrderRolled = (priv && priv.orderRoll) ? true : false,
hasRolled = (turn && turn.roll) ? true : false,
isTurn = (turn && turn.color === color) ? true : false,
robberActions = (turn && turn.robberInAction),
haveResources = player ? player.haveResources : false,
haveResources = priv ? priv.resources !== 0 : false,
placement = (state === 'initial-placement' || (turn && turn.active === 'road-building')),
placeRoad = placement && turn && turn.actions && turn.actions.indexOf('place-road') !== -1;
@ -169,7 +159,7 @@ const Actions = () => {
} onClick={rollClick}>Roll Dice</Button>
<Button disabled={placeRoad || robberActions || !isTurn || !hasRolled || !haveResources} onClick={tradeClick}>Trade</Button>
<Button disabled={placeRoad || robberActions || !isTurn || !hasRolled || !haveResources} onClick={buildClicked}>Build</Button>
{ turn && turn.roll === 7 && player && player.mustDiscard > 0 &&
{ turn && turn.roll === 7 && priv && priv.mustDiscard > 0 &&
<Button onClick={discardClick}>Discard</Button>
}
<Button disabled={placeRoad || robberActions || !isTurn || !hasRolled} onClick={passClick}>Done</Button>

View File

@ -46,7 +46,6 @@ const Activities = () => {
const [activities, setActivities] = useState([]);
const [turn, setTurn] = useState();
const [color, setColor] = useState();
const [player, setPlayer] = useState(undefined);
const [players, setPlayers] = useState({});
const [timestamp, setTimestamp] = useState(0);
const [state, setState] = useState('');
@ -99,11 +98,6 @@ const Activities = () => {
if ('players' in data.update
&& !equal(data.update.players, players)) {
setPlayers(data.update.players);
if ((color in data.update.players)
&& !equal(player, data.update.players[color])) {
setPlayer(data.update.players[color]);
requestUpdate('turn');
}
}
if ('timestamp' in data.update
&& data.update.timestamp !== timestamp) {
@ -111,11 +105,6 @@ const Activities = () => {
}
if ('color' in data.update && data.update.color !== color) {
setColor(data.update.color);
if (players && (data.update.color in players)
&& !equal(player, players[data.update.color])) {
setPlayer(player);
requestUpdate('turn');
}
}
break;
default:

View File

@ -51,54 +51,6 @@ const Board = () => {
'placements', 'turn', 'state', 'color', 'longestRoadLength'
], []);
/* Placements is a structure of roads and corners arrays
* indicating what is stored at each of the locations
*
* Corners consist of a type and color
* Roads consist of a color, and longestRoad
*
* See games.js resetGame, placeRoad, placeSettlement, placeCity,
* and calculateRoadLengths
*
* Returns: true === differences, false === same
*/
const comparePlacements = (A, B) => {
if (!A && !B) {
return false; /* same */
}
if ((A && !B)
|| (!A && B)) {
return true;
}
if ((A.roads.length !== B.roads.length)
|| (A.corners.length !== B.corners.length)) {
return true;
}
/* Roads compare color and longestRoad */
for (let i = 0; i < A.roads.length; i++) {
if (A.roads[i].color !== B.roads[i].color) {
return true;
}
if (A.roads[i].longestRoad !== B.roads[i].longestRoad) {
return true;
}
}
/* Corners compare type and color */
for (let i = 0; i < A.corners.length; i++) {
if (A.corners[i].type !== B.corners[i].type) {
return true;
}
if (A.corners[i].color !== B.corners[i].color) {
return true;
}
}
return false; /* same */
};
const onWsMessage = (event) => {
const data = JSON.parse(event.data);
switch (data.type) {
@ -132,7 +84,7 @@ const Board = () => {
}
}
if ('placement' in data.update) {
if ('placements' in data.update) {
if (!equal(data.update.placements, placements)) {
console.log(`board - placements`, data.update.placements);
setPlacements(data.update.placements);

View File

@ -1,7 +1,8 @@
import React, { useState, useEffect, useContext, useRef, useMemo } from "react";
import "./Actions.css";
import Paper from '@material-ui/core/Paper';
import Button from '@material-ui/core/Button';
import equal from "fast-deep-equal";
import { Dice } from "./Dice.js";
import { PlayerColor } from "./PlayerColor.js";
@ -11,25 +12,23 @@ import { GlobalContext } from "./GlobalContext.js";
const GameOrder = () => {
const { ws } = useContext(GlobalContext);
const [player, setPlayer] = useState(undefined);
const [players, setPlayers] = useState({});
const [color, setColor] = useState(undefined);
const fields = useMemo(() => [
'player', 'players'
'players', 'color'
], []);
const color = player ? player.color : undefined;
const onWsMessage = (event) => {
const data = JSON.parse(event.data);
switch (data.type) {
case 'game-update':
console.log(`GameOrder game-update: `, data.update);
if ('player' in data.update) {
setPlayer(data.update.player);
}
if ('players' in data.update) {
if ('players' in data.update && !equal(players, data.update.players)) {
setPlayers(data.update.players);
}
if ('color' in data.update && data.update.color !== color) {
setColor(data.update.color);
}
break;
default:
break;

View File

@ -458,11 +458,11 @@ const Trade = ({table}) => {
<div className="PlayerList">
{ players }
</div>
{ !player.haveResources && <b>You have no resources to participate in this trade.</b> }
{ player.resources === 0 && <b>You have no resources to participate in this trade.</b> }
<Button disabled={isOfferSubmitted || !isOfferValid}
onClick={offerClicked}>Offer</Button>
{ player.haveResources &&
{ player.resources !== 0 &&
<div className="Transfers">
<div className="GiveGet"><div>Get</div><div>Give</div><div>Have</div></div>
{ transfers }

View File

@ -202,7 +202,7 @@ const processGameOrder = (game, player, dice) => {
name: players[0].name,
color: players[0].color
};
placeSettlement(game, getValidCorners(game));
getValidSettlementPlacements(game, getValidCorners(game));
addActivity(game, null, `${game.robberName} Robber Robinson entered the scene as the nefarious robber!`);
addChatMessage(game, null, `Initial settlement placement has started!`);
addChatMessage(game, null, `It is ${game.turn.name}'s turn to place a settlement.`);4
@ -318,6 +318,7 @@ const distributeResources = (game, roll) => {
if (color !== 'robber') {
session = sessionFromColor(game, color);
session.player[type] += entry[type];
session.player.resources += entry[type];
message.push(`${entry[type]} ${type}`);
} else {
robberSteal(game, color, type);
@ -414,6 +415,7 @@ const newPlayer = (color) => {
points: 0,
status: "Not active",
lastActive: 0,
resources: 0,
order: 0,
stone: 0,
wheat: 0,
@ -654,7 +656,7 @@ const adminActions = (game, action, value) => {
return `There are no valid locations for ${game.turn.name} to place a settlement.`;
}
game.turn.free = true;
placeSettlement(game, corners);
getValidSettlementPlacements(game, corners);
addChatMessage(game, null, `Admin gave a settlment to ${game.turn.name}. ` +
`They must now place the settlement.`);
break;
@ -665,6 +667,7 @@ const adminActions = (game, action, value) => {
case 'brick':
const count = parseInt(card);
session.player[type] += count;
session.resources += count;
addChatMessage(game, null, `Admin gave ${count} ${type} to ${game.turn.name}.`);
break;
default:
@ -1627,7 +1630,7 @@ const placeCity = (game, limits) => {
game.turn.limits = { corners: limits };
}
const placeSettlement = (game, limits) => {
const getValidSettlementPlacements = (game, limits) => {
game.turn.actions = [ 'place-settlement' ];
game.turn.limits = { corners: limits };
}
@ -1799,14 +1802,18 @@ const trade = (game, session, { offer, value }) => {
offer.gets.forEach(item => {
if (target.name !== 'The bank') {
target[item.type] -= item.count;
target.resources -= item.count;
}
player[item.type] += item.count;
player.resources += item.count;
});
offer.gives.forEach(item => {
if (target.name !== 'The bank') {
target[item.type] += item.count;
target.resources += item.count;
}
player[item.type] -= item.count;
player.resources -= item.count;
});
const from = (offer.name === 'The bank') ? 'the bank' : offer.name;
@ -1943,7 +1950,9 @@ const stealResource = (game, session, value) => {
let index = Math.floor(Math.random() * cards.length),
type = cards[index];
victim[type]--;
session.player[type]++
victim.resources--;
session.player[type]++;
session.player.resources++;
game.turn.actions = [];
game.turn.limits = {};
addChatMessage(game, session,
@ -1988,6 +1997,10 @@ const buyDevelopment = (game, session, value) => {
player.stone--;
player.wheat--;
player.sheep--;
player.resources = 0;
[ 'wheat', 'brick', 'sheep', 'stone', 'wood' ].forEach(resource => {
player.resources += player[resource];
});
debugChat(game, 'After development purchase');
card = game.developmentCards.pop();
card.turn = game.turns;
@ -2104,6 +2117,131 @@ const playCard = (game, session, { card }) => {
}
}
const placeSettlement = (game, session, value) => {
const player = session.player;
if (game.state !== 'initial-placement' && 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.`;
}
index = parseInt(value);
if (game.placements.corners[index] === undefined) {
return `You have requested to place a settlement illegally!`;
}
/* If this is not a valid road in 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.`;
}
corner = game.placements.corners[index];
if (corner.color) {
return `This location already has a settlement belonging to ${game.players[corner.color].name}!`;
}
if (!player.banks) {
player.banks = [];
}
if (game.state === 'normal') {
if (!game.turn.free) {
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.`;
}
player.settlements--;
if (!game.turn.free) {
addChatMessage(game, session, `${session.name} spent 1 brick, 1 wood, 1 sheep, 1 wheat to purchase a settlement.`)
player.brick--;
player.wood--;
player.wheat--;
player.sheep--;
player.resources = 0;
[ 'wheat', 'brick', 'sheep', 'stone', 'wood' ].forEach(resource => {
player.resources += player[resource];
});
}
delete game.turn.free;
corner.color = session.color;
corner.type = 'settlement';
let bankType = undefined;
if (layout.corners[index].banks.length) {
layout.corners[index].banks.forEach(bank => {
const border = game.borderOrder[Math.floor(bank / 3)],
type = game.borders[border][bank % 3];
console.log(`${session.id}: Bank ${bank} = ${type}`);
if (!type) {
console.log(`${session.id}: Bank ${bank}`)
return;
}
bankType = (type === 'bank')
? '3 of anything for 1 resource'
: `2 ${type} for 1 resource`;
if (player.banks.indexOf(type) === -1) {
player.banks.push(type);
}
});
}
game.turn.actions = [];
game.turn.limits = {};
if (bankType) {
addActivity(game, session,
`${session.name} placed a settlement by a maritime bank that trades ${bankType}.`);
} else {
addActivity(game, session, `${session.name} placed a settlement.`);
}
calculateRoadLengths(game, session);
} else if (game.state === 'initial-placement') {
if (game.direction && game.direction === 'backward') {
session.initialSettlement = index;
}
corner.color = session.color;
corner.type = 'settlement';
let bankType = undefined;
if (layout.corners[index].banks.length) {
layout.corners[index].banks.forEach(bank => {
const border = game.borderOrder[Math.floor(bank / 3)],
type = game.borders[border][bank % 3];
console.log(`${session.id}: Bank ${bank} = ${type}`);
if (!type) {
return;
}
bankType = (type === 'bank')
? '3 of anything for 1 resource'
: `2 ${type} for 1 resource`;
if (player.banks.indexOf(type) === -1) {
player.banks.push(type);
}
});
}
player.settlements--;
if (bankType) {
addActivity(game, session,
`${session.name} placed a settlement by a maritime bank that trades ${bankType}. ` +
`Next, they need to place a road.`);
} else {
addActivity(game, session, `${name} placed a settlement. ` +
`Next, they need to place a road.`);
}
placeRoad(game, layout.corners[index].roads);
}
sendUpdateToPlayers(game, {
placements: game.placements,
turn: game.turn,
});
sendUpdateToPlayer(session, {
private: game.player
});
}
const asdf = () => {
const game = 0, session = 0;
switch (game) {
@ -2179,6 +2317,7 @@ const asdf = () => {
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;
}
@ -2195,6 +2334,7 @@ const asdf = () => {
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.`);
@ -2236,132 +2376,10 @@ const asdf = () => {
error = `There are no valid locations for you to place a settlement.`;
break;
}
placeSettlement(game, corners);
getValidSettlementPlacements(game, corners);
addActivity(game, session, `${game.turn.name} is considering placing a settlement.`);
break;
case 'place-settlement':
if (game.state !== 'initial-placement' && 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 settlement illegally!`;
break;
}
/* If this is not a valid road in 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) {
error = `This location already has a settlement belonging to ${game.players[corner.color].name}!`;
break;
}
if (!player.banks) {
player.banks = [];
}
if (game.state === 'normal') {
if (!game.turn.free) {
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;
}
debugChat(game, 'Before settlement purchase');
player.settlements--;
if (!game.turn.free) {
addChatMessage(game, session, `${session.name} spent 1 brick, 1 wood, 1 sheep, 1 wheat to purchase a settlement.`)
player.brick--;
player.wood--;
player.wheat--;
player.sheep--;
}
delete game.turn.free;
debugChat(game, 'After settlement purchase');
corner.color = session.color;
corner.type = 'settlement';
let bankType = undefined;
if (layout.corners[index].banks.length) {
layout.corners[index].banks.forEach(bank => {
const border = game.borderOrder[Math.floor(bank / 3)],
type = game.borders[border][bank % 3];
console.log(`Bank ${bank} = ${type}`);
if (!type) {
console.log(`Bank ${bank}`)
return;
}
bankType = (type === 'bank')
? '3 of anything for 1 resource'
: `2 ${type} for 1 resource`;
if (player.banks.indexOf(type) === -1) {
player.banks.push(type);
}
});
}
game.turn.actions = [];
game.turn.limits = {};
if (bankType) {
addActivity(game, session,
`${name} placed a settlement by a maritime bank that trades ${bankType}.`);
} else {
addActivity(game, session, `${name} placed a settlement.`);
}
calculateRoadLengths(game, session);
} else if (game.state === 'initial-placement') {
if (game.direction && game.direction === 'backward') {
session.initialSettlement = index;
}
corner.color = session.color;
corner.type = 'settlement';
let bankType = undefined;
if (layout.corners[index].banks.length) {
layout.corners[index].banks.forEach(bank => {
console.log(game.borderOrder);
console.log(game.borders);
const border = game.borderOrder[Math.floor(bank / 3)],
type = game.borders[border][bank % 3];
console.log(`Bank ${bank} = ${type}`);
if (!type) {
return;
}
bankType = (type === 'bank')
? '3 of anything for 1 resource'
: `2 ${type} for 1 resource`;
if (player.banks.indexOf(type) === -1) {
player.banks.push(type);
}
});
}
player.settlements--;
if (bankType) {
addActivity(game, session,
`${name} placed a settlement by a maritime bank that trades ${bankType}. ` +
`Next, they need to place a road.`);
} else {
addActivity(game, session, `${name} placed a settlement. ` +
`Next, they need to place a road.`);
}
placeRoad(game, layout.corners[index].roads);
}
break;
case 'buy-city':
if (game.state !== 'normal') {
error = `You cannot purchase a city unless the game is active.`;
@ -2447,6 +2465,10 @@ const asdf = () => {
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;
@ -2536,6 +2558,10 @@ const asdf = () => {
addChatMessage(game, session, `${name} spent 1 brick, 1 wood to purchase a road.`)
player.brick--;
player.wood--;
player.resources = 0;
[ 'wheat', 'brick', 'sheep', 'stone', 'wood' ].forEach(resource => {
player.resources += player[resource];
});
}
debugChat(game, 'After road purchase');
@ -2590,7 +2616,7 @@ const asdf = () => {
name: next,
color: getColorFromName(game, next)
};
placeSettlement(game, getValidCorners(game));
getValidSettlementPlacements(game, getValidCorners(game));
calculateRoadLengths(game, session);
addChatMessage(game, null, `It is ${next}'s turn to place a settlement.`);
} else {
@ -2623,6 +2649,7 @@ const asdf = () => {
let message = [];
for (let type in receives) {
player[type] += receives[type];
player.resources += receives[type];
message.push(`${receives[type]} ${type}`);
}
addChatMessage(game, session, `${session.name} receives ${message.join(', ')}.`);
@ -2653,8 +2680,10 @@ const asdf = () => {
break;
}
for (let type in discards) {
player[type] -= parseInt(discards[type]);
player.mustDiscard -= parseInt(discards[type])
const count = parseInt(discards[type]);
player[type] -= count;
player.mustDiscard -= count;
player.resources -= count;
}
addChatMessage(game, null, `${session.name} discarded ${sum} resource cards.`);
if (player.mustDiscard) {
@ -3002,6 +3031,24 @@ router.ws("/ws/:id", (ws, req) => {
});
});
const getFilteredPlayers = (game) => {
const filtered = {};
for (let color in game.players) {
const player = Object.assign({}, game.players[color]);
filtered[color] = player;
if (player.status === 'Not active') {
continue;
}
player.resources = 0;
[ 'wheat', 'brick', 'sheep', 'stone', 'wood' ].forEach(resource => {
player.resources += player[resource];
delete player[resource];
});
delete player.development;
}
return filtered;
};
const wsConnect = async (ws, req) => {
const { id } = req.params;
const gameId = id;
@ -3179,6 +3226,10 @@ const wsConnect = async (ws, req) => {
update = {};
data.fields.forEach((field) => {
switch (field) {
case 'player':
sendWarning(session, `'player' is not a valid item. use 'private' instead`);
update.player = undefined;
break;
case 'id':
case 'chat':
case 'startTime':
@ -3191,8 +3242,11 @@ const wsConnect = async (ws, req) => {
case 'unselected':
update.unselected = getFilteredUnselected(game);
break;
case 'private':
update.private = session.player;
break;
case 'players':
update.players = game.players;
update.players = getFilteredPlayers(game);
break;
case 'color':
console.log(`${session.id}: -> Returning color as ${session.color} for ${getName(session)}`);
@ -3343,14 +3397,6 @@ const debugChat = (game, preamble) => {
const sendGameToSession = (session, reducedSessions, game, reducedGame, error, res) => {
const player = session.player ? session.player : undefined;
if (player) {
player.haveResources = player.wheat > 0 ||
player.brick > 0 ||
player.sheep > 0 ||
player.stone > 0 ||
player.wood > 0;
}
/* Strip out data that should not be shared with players */
delete reducedGame.developmentCards;
@ -3577,14 +3623,6 @@ const getFilteredGameForPlayer = (game, session) => {
const player = session.player ? session.player : undefined;
if (player) {
player.haveResources = player.wheat > 0 ||
player.brick > 0 ||
player.sheep > 0 ||
player.stone > 0 ||
player.wood > 0;
}
/* Strip out data that should not be shared with players */
delete reducedGame.developmentCards;
@ -3653,11 +3691,6 @@ const resetGame = (game) => {
longestRoad: undefined
});
/* Reset all player data */
for (let color in game.players) {
clearPlayer(game.players[color]);
}
/* Populate the game corner and road placement data as cleared */
for (let i = 0; i < layout.corners.length; i++) {
game.placements.corners[i] = {
@ -3673,6 +3706,14 @@ const resetGame = (game) => {
};
}
/* Put the robber back on the Desert */
for (let i = 0; i < game.pipOrder.length; i++) {
if (game.pipOrder[i] === 18) {
game.robber = i;
break;
}
}
/* Populate the game development cards with a fresh deck */
for (let i = 1; i <= 14; i++) {
game.developmentCards.push({
@ -3695,6 +3736,11 @@ const resetGame = (game) => {
shuffleArray(game.developmentCards);
/* Reset all player data */
for (let color in game.players) {
clearPlayer(game.players[color]);
}
/* Ensure sessions are connected to player objects */
for (let key in game.sessions) {
const session = game.sessions[key];
@ -3702,14 +3748,7 @@ const resetGame = (game) => {
session.player = game.players[session.color];
session.player.status = 'Active';
session.player.lastActive = Date.now();
}
}
/* Put the robber back on the Desert */
for (let i = 0; i < game.pipOrder.length; i++) {
if (game.pipOrder[i] === 18) {
game.robber = i;
break;
session.player.live = session.live;
}
}
}