1
0

Game seems to be working

Signed-off-by: James Ketrenos <james_eikona@ketrenos.com>
This commit is contained in:
James Ketrenos 2022-03-14 00:07:50 -07:00
parent 81df996e48
commit 42d076f216
7 changed files with 331 additions and 169 deletions

View File

@ -141,7 +141,7 @@ const Activities = () => {
placeRoad = placement && turn && turn.actions && turn.actions.indexOf('place-road') !== -1, placeRoad = placement && turn && turn.actions && turn.actions.indexOf('place-road') !== -1,
mustStealResource = turn && turn.actions && turn.actions.indexOf('steal-resource') !== -1, mustStealResource = turn && turn.actions && turn.actions.indexOf('steal-resource') !== -1,
rollForOrder = (state === 'game-order'); rollForOrder = (state === 'game-order');
console.log(`activities - `, state, turn); console.log(`activities - `, state, turn, activities);
let discarders = [], mustDiscard = false; let discarders = [], mustDiscard = false;
for (let key in players) { for (let key in players) {
const player = players[key]; const player = players[key];

View File

@ -129,7 +129,16 @@ body {
.Table .ViewCard { .Table .ViewCard {
display: flex; display: flex;
position: relative; position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.Table .Winner {
display: flex;
position: absolute;
top: 0; top: 0;
left: 0; left: 0;
right: 0; right: 0;

View File

@ -25,6 +25,7 @@ import { ViewCard } from "./ViewCard.js";
import { ChooseCard } from "./ChooseCard.js"; import { ChooseCard } from "./ChooseCard.js";
import { Hand } from "./Hand.js"; import { Hand } from "./Hand.js";
import { Trade } from "./Trade.js"; import { Trade } from "./Trade.js";
import { Winner } from "./Winner.js";
import history from "./history.js"; import history from "./history.js";
import "./App.css"; import "./App.css";
@ -305,8 +306,7 @@ const Table = () => {
</div> } </div> }
{ state === 'normal' && <SelectPlayer/> } { state === 'normal' && <SelectPlayer/> }
{ color && state === 'game-order' && <GameOrder/> } { color && state === 'game-order' && <GameOrder/> }
<Winner/>
{ /* state === 'winner' && <Winner color={winner}/> */ }
<ViewCard {...{cardActive, setCardActive }}/> <ViewCard {...{cardActive, setCardActive }}/>
<ChooseCard/> <ChooseCard/>
</div> </div>

View File

@ -384,7 +384,7 @@ const Trade = () => {
} else { } else {
const found = activeTrades.find(item => item.name === turn.name); const found = activeTrades.find(item => item.name === turn.name);
if (found) { if (found) {
found.valid = !(color in found.offerRejected) && isCompatibleOffer(priv, found); found.valid = !(color in found.offerRejected) && canMeetOffer(priv, found);
} }
} }

View File

@ -84,8 +84,10 @@ const ViewCard = ({cardActive, setCardActive}) => {
lookup = cardActive.type; lookup = cardActive.type;
} }
let cardName = '';
switch (lookup) { switch (lookup) {
case 'army': case 'army':
cardName = 'Knight';
description = <> description = <>
<div>When played, you <b>must</b> move the robber.</div> <div>When played, you <b>must</b> move the robber.</div>
<div>Steal <b>1</b> resource card from the owner of an adjacent settlement or city.</div> <div>Steal <b>1</b> resource card from the owner of an adjacent settlement or city.</div>
@ -93,6 +95,7 @@ const ViewCard = ({cardActive, setCardActive}) => {
knight or one progress card.</div></>; knight or one progress card.</div></>;
break; break;
case 'vp': case 'vp':
cardName = `Victory Point: ${capitalize(cardActive.card)}`;
description = <><div><b>1</b> victory point.</div> description = <><div><b>1</b> victory point.</div>
<div>You only reveal your victory point cards when the game is over, either <div>You only reveal your victory point cards when the game is over, either
when you or an opponent when you or an opponent
@ -101,6 +104,7 @@ const ViewCard = ({cardActive, setCardActive}) => {
break; break;
case 'progress-road-1': case 'progress-road-1':
case 'progress-road-2': case 'progress-road-2':
cardName = 'Road Building'
description = <> description = <>
<div>Play <b>2</b> new roads as if you had just built them.</div> <div>Play <b>2</b> new roads as if you had just built them.</div>
<div>This is still limited by the number of roads you have. If you do not have enough roads <div>This is still limited by the number of roads you have. If you do not have enough roads
@ -110,12 +114,14 @@ const ViewCard = ({cardActive, setCardActive}) => {
</>; </>;
break; break;
case 'progress-monopoly': case 'progress-monopoly':
cardName = 'Monopoly';
description = <> description = <>
<div>When you play this card, you will select <b>1</b> type of resource. <div>When you play this card, you will select <b>1</b> type of resource.
All other players must give you all their resource cards of that type.</div> All other players must give you all their resource cards of that type.</div>
</>; </>;
break; break;
case 'progress-year-of-plenty': case 'progress-year-of-plenty':
cardName = 'Year of Plenty';
description = <> description = <>
<div>Take any <b>2</b> resources from the bank. Add them to your hand. They can be <div>Take any <b>2</b> resources from the bank. Add them to your hand. They can be
<b>2</b> of the same resource or <b>1</b> of two differ resources.</div> <b>2</b> of the same resource or <b>1</b> of two differ resources.</div>
@ -159,7 +165,7 @@ const ViewCard = ({cardActive, setCardActive}) => {
return ( return (
<div className="ViewCard"> <div className="ViewCard">
<Paper> <Paper>
<div className="Title">{capitalize(cardActive.type)}</div> <div className="Title">{cardName}</div>
<div style={{display: 'flex', flexDirection: 'row'}}> <div style={{display: 'flex', flexDirection: 'row'}}>
<Resource type={`${cardActive.type}-${cardActive.card}`} disabled count={1}/> <Resource type={`${cardActive.type}-${cardActive.card}`} disabled count={1}/>
<div className="Description">{description}</div> <div className="Description">{description}</div>

View File

@ -1,54 +1,95 @@
import React from "react"; import React, { useState, useEffect, useContext, useRef, useMemo, useCallback } from "react";
import "./Winner.css"; import equal from "fast-deep-equal";
import Paper from '@material-ui/core/Paper'; import Paper from '@material-ui/core/Paper';
import Button from '@material-ui/core/Button'; import Button from '@material-ui/core/Button';
import Resource from './Resource.js';
import { getPlayerName } from './Common.js';
import PlayerColor from './PlayerColor.js';
const Winner = ({table, color}) => { import "./Winner.css";
const quitClicked = (event) => {
table.setSelected("");
}
if (!table.game) { import {Resource} from './Resource.js';
import {PlayerColor} from './PlayerColor.js';
import { GlobalContext } from "./GlobalContext.js";
const Winner = () => {
const { ws } = useContext(GlobalContext);
const [winner, setWinner] = useState(undefined);
const [state, setState] = useState(undefined);
const fields = useMemo(() => [
'winner', 'state'
], []);
const onWsMessage = (event) => {
const data = JSON.parse(event.data);
switch (data.type) {
case 'game-update':
console.log(`winner - game update`, data.update);
if ('winner' in data.update && !equal(data.update.winner, winner)) {
setWinner(data.update.winner);
}
if ('state' in data.update && data.update.state !== state) {
if (data.update.state !== 'winner') {
setWinner(undefined);
}
setState(data.update.state);
}
break;
default:
break;
}
};
const refWsMessage = useRef(onWsMessage);
useEffect(() => { refWsMessage.current = onWsMessage; });
useEffect(() => {
if (!ws) { return; }
const cbMessage = e => refWsMessage.current(e);
ws.addEventListener('message', cbMessage);
return () => {
ws.removeEventListener('message', cbMessage);
}
}, [ws, refWsMessage]);
useEffect(() => {
if (!ws) { return; }
ws.send(JSON.stringify({
type: 'get',
fields
}));
}, [ws, fields]);
const quitClicked = useCallback((event) => {
ws.send(JSON.stringify({
type: 'goto-lobby'
}));
}, [ws]);
if (!winner) {
return <></>; return <></>;
} }
const game = table.game; let losers = [];
for (let key in winner.players) {
const name = getPlayerName(game.sessions, color), if (key === winner.color || winner.players[key].status === 'Not active') {
player = game.players[color];
let playerCount = 0;
for (let key in game.players) {
if (game.players[key].status !== 'Not active') {
playerCount++;
}
}
let description = <>
<div>Congratulations, <b>{name}</b>!</div>
<div><PlayerColor color={color}/> {name} won the game with <b>{player.points}</b> after {Math.floor(game.turns / playerCount)} turns.
{ Number(player.potential) !== 0 && <>They had <b>{player.potential}</b> unplayed Victory Point card(s).</>}</div>
</>;
for (let key in game.players) {
if (key === color) {
continue; continue;
} }
let tmp = game.players[key]; losers.push(winner.players[key]);
if (tmp.status === 'Not active') {
continue;
}
let line = <><PlayerColor color={key}/> {getPlayerName(game.sessions, key)} finished with {tmp.points} victory points.
{ Number(tmp.potential) !== 0 && <>They had <b>{tmp.potential}</b> unplayed Victory Point card(s).</> }</>;
description = <>{description}<div>{line}</div></>;
} }
if (game.robberStole) { const turnCount = Math.floor(winner.turns / (losers.length + 1));
losers = losers.map(player => {
const averageSeconds = Math.floor(player.totalTime / turnCount / 1000),
average = `${Math.floor(averageSeconds / 60)}m:${averageSeconds % 60}s`;
return <div key={player.color}>
<PlayerColor color={player.color}/> {player.name} finished with {player.points} victory points.
{ Number(player.potential) !== 0 && <>They had <b>{player.potential}</b> unplayed Victory Point card(s).</> }
Their average turn time was {average}.
</div>;
});
let robber = <></>;
if (winner.robberStole) {
let stolen = <></>; let stolen = <></>;
for (let type in game.stolen) { for (let type in winner.stolen) {
const resource = game.stolen[type]; const resource = winner.stolen[type];
if (typeof resource === 'object') { /* player colors are also in 'game.stolen' */ if (typeof resource === 'object') { /* player colors are also in 'game.stolen' */
continue; continue;
} }
@ -56,22 +97,41 @@ const Winner = ({table, color}) => {
<Resource label={true} type={type} count={resource}/> <Resource label={true} type={type} count={resource}/>
</>; </>;
} }
description = <>{description}<div> robber = <div>
Throughout the game, the robber stole <b>{game.robberStole}</b> resources: Throughout the game, the robber stole <b>{winner.robberStole}</b> resources:
<div>{stolen}</div> <div>{stolen}</div>
</div></>; </div>;
} }
description = <>{description}<div>If everyone goes back to the Lobby, you can play again.</div></>; const averageSeconds = Math.floor(winner.totalTime / turnCount / 1000),
average = `${Math.floor(averageSeconds / 60)}m:${averageSeconds % 60}s`;
const seconds = winner.elapsedTime / 1000,
h = Math.floor(seconds / (60 * 60)),
m = Math.floor((seconds % (60 * 60)) / 60),
s = Math.floor((seconds % (60 * 60)) % 60);
const totalTime = `${h}h:${m}m:${s}s`;
let vpType = ['market', 'university', 'library', 'palace'];
vpType = vpType[Math.floor(vpType.length * Math.random())];
return ( return (
<div className="Winner"> <div className="Winner">
<Paper> <Paper>
<div className="Title">{name} has won with {player.points} victory points!</div> <div className="Title">{winner.name} has won with {winner.points} victory points!</div>
<div style={{display: 'flex', flexDirection: 'row'}}> <div style={{display: 'flex', flexDirection: 'row'}}>
<Resource type={`vp-palace`} disabled count={1}/> <Resource type={`vp-${vpType}`} disabled count={1}/>
<div className="Description"> <div className="Description">
{description} <div>Congratulations, <b>{winner.name}</b>!</div>
<div>
<PlayerColor color={winner.color}/> {winner.name} won the game
with <b>{winner.points}</b> Victory Points after {turnCount} game turns.
{ Number(winner.potential) !== 0 && <>They had <b>{winner.potential}</b> unplayed Victory Point card(s).</>}
Their average turn time was {average}.
</div>
{ losers }
<div>The game took {totalTime}.</div>
{ robber }
</div> </div>
</div> </div>
<Button onClick={quitClicked}>Go back to Lobby</Button> <Button onClick={quitClicked}>Go back to Lobby</Button>
@ -80,4 +140,4 @@ const Winner = ({table, color}) => {
); );
}; };
export default Winner; export { Winner };

View File

@ -18,7 +18,7 @@ const debug = {
audio: false, audio: false,
get: true, get: true,
set: true, set: true,
update: true update: false
}; };
let gameDB; let gameDB;
@ -222,6 +222,7 @@ const roll = (game, session) => {
if (player.order && player.orderRoll) { if (player.order && player.orderRoll) {
return `Player ${name} has already rolled for player order.`; return `Player ${name} has already rolled for player order.`;
} }
game.startTime = Date.now();
const dice = Math.ceil(Math.random() * 6); const dice = Math.ceil(Math.random() * 6);
addChatMessage(game, session, `${name} rolled ${dice}.`); addChatMessage(game, session, `${name} rolled ${dice}.`);
return processGameOrder(game, player, dice); return processGameOrder(game, player, dice);
@ -308,7 +309,7 @@ const distributeResources = (game, roll) => {
session.player.resources += entry[type]; session.player.resources += entry[type];
message.push(`${entry[type]} ${type}`); message.push(`${entry[type]} ${type}`);
} else { } else {
robberSteal(game, color, type); robberSteal(game, color, type, entry[type]);
robber.push(`${entry[type]} ${type}`); robber.push(`${entry[type]} ${type}`);
} }
} }
@ -345,7 +346,7 @@ const processRoll = (game, session, dice) => {
distributeResources(game, game.turn.roll); distributeResources(game, game.turn.roll);
for (let id in game.sessions) { for (let id in game.sessions) {
if (game.sessions[id].player) { if (game.sessions[id].player) {
sendUpdateToPlayer(game.sessions[id], { sendUpdateToPlayer(game, game.sessions[id], {
private: game.sessions[id].player private: game.sessions[id].player
}); });
} }
@ -394,7 +395,7 @@ const processRoll = (game, session, dice) => {
addChatMessage(game, null, `The robber was rolled and ${player.name} must discard ${player.mustDiscard} resource cards!`); addChatMessage(game, null, `The robber was rolled and ${player.name} must discard ${player.mustDiscard} resource cards!`);
for (let key in game.sessions) { for (let key in game.sessions) {
if (game.sessions[key].player === player) { if (game.sessions[key].player === player) {
sendUpdateToPlayer(game.sessions[key], { sendUpdateToPlayer(game, game.sessions[key], {
private: player private: player
}); });
break; break;
@ -428,7 +429,9 @@ const newPlayer = (color) => {
army: 0, army: 0,
development: [], development: [],
color: color, color: color,
name: "" name: "",
totalTime: 0,
turnStart: 0
}; };
} }
@ -556,7 +559,7 @@ const loadGame = async (id) => {
}; };
const clearPlayer = (player) => { const clearPlayer = (player) => {
const { color } = player; const color = player.color;
for (let key in player) { for (let key in player) {
delete player[key]; delete player[key];
} }
@ -729,8 +732,8 @@ const adminActions = (game, action, value) => {
let name = game.turn.name; let name = game.turn.name;
const next = getNextPlayer(game, name); const next = getNextPlayer(game, name);
game.turn = { game.turn = {
name: next, name: next.player,
color: getColorFromName(game, next) color: next.color
}; };
game.turns++; game.turns++;
addChatMessage(game, null, `The admin skipped ${name}'s turn.`); addChatMessage(game, null, `The admin skipped ${name}'s turn.`);
@ -861,7 +864,7 @@ const setPlayerName = (game, session, name) => {
} }
} }
sendUpdateToPlayer(session, { sendUpdateToPlayer(game, session, {
name: session.name, name: session.name,
color: session.color, color: session.color,
live: session.live, live: session.live,
@ -940,7 +943,7 @@ const setPlayerColor = (game, session, color) => {
addChatMessage(game, null, addChatMessage(game, null,
`There are no longer enough players to start a game.`); `There are no longer enough players to start a game.`);
} }
sendUpdateToPlayer(session, { sendUpdateToPlayer(game, session, {
name: session.name, name: session.name,
color: '', color: '',
live: session.live, live: session.live,
@ -993,7 +996,7 @@ const setPlayerColor = (game, session, color) => {
update.active = game.active; update.active = game.active;
} }
sendUpdateToPlayer(session, { sendUpdateToPlayer(game, session, {
name: session.name, name: session.name,
color: session.color, color: session.color,
live: session.live, live: session.live,
@ -1070,17 +1073,22 @@ const getNextPlayer = (game, name) => {
break; break;
} }
} }
if (!color) { console.log(`current player is ${color}`);
return name;
}
let index = game.playerOrder.indexOf(color); let index = game.playerOrder.indexOf(color);
console.log(`current player is ${color} ${index}`);
index = (index + 1) % game.playerOrder.length; index = (index + 1) % game.playerOrder.length;
console.log(`current player is ${color} ${index}`);
color = game.playerOrder[index];
console.log(`current player is ${color} ${index}`);
for (let id in game.sessions) { for (let id in game.sessions) {
if (game.sessions[id].color === game.playerOrder[index]) { if (game.sessions[id].color === color) {
return game.sessions[id].name; return game.sessions[id].player;
} }
} }
return name; console.log(`nothing found... returning current player.`);
console.log(game.players);
return player;
} }
const getPrevPlayer = (game, name) => { const getPrevPlayer = (game, name) => {
@ -1091,17 +1099,14 @@ const getPrevPlayer = (game, name) => {
break; break;
} }
} }
if (!color) {
return name;
}
let index = game.playerOrder.indexOf(color); let index = game.playerOrder.indexOf(color);
index = (index - 1) % game.playerOrder.length; index = (index - 1) % game.playerOrder.length;
for (let id in game.sessions) { for (let id in game.sessions) {
if (game.sessions[id].color === game.playerOrder[index]) { if (game.sessions[id].color === game.playerOrder[index]) {
return game.sessions[id].name; return game.sessions[id].player;
} }
} }
return name; return player;
} }
const processCorner = (game, color, cornerIndex, placedCorner) => { const processCorner = (game, color, cornerIndex, placedCorner) => {
@ -1856,14 +1861,16 @@ const pass = (game, session) => {
return `Robber is in action. Turn can not stop until all Robber tasks are resolved.`; return `Robber is in action. Turn can not stop until all Robber tasks are resolved.`;
} }
const next = getNextPlayer(game, name); const next = getNextPlayer(game, session.name);
session.player.totalTime += Date.now() - session.player.turnStart;
game.turn = { game.turn = {
name: next, name: next.name,
color: getColorFromName(game, next) color: next.color
}; };
next.turnStart = Date.now();
game.turns++; game.turns++;
addActivity(game, session, `${name} passed their turn.`); addActivity(game, session, `${name} passed their turn.`);
addChatMessage(game, null, `It is ${next}'s turn.`); addChatMessage(game, null, `It is ${next.name}'s turn.`);
sendUpdateToPlayers(game, { sendUpdateToPlayers(game, {
turns: game.turns, turns: game.turns,
@ -1936,7 +1943,7 @@ const placeRobber = (game, session, robber) => {
robberName: game.robberName, robberName: game.robberName,
activities: game.activities activities: game.activities
}); });
sendUpdateToPlayer(session, { sendUpdateToPlayer(game, session, {
private: session.player private: session.player
}); });
} }
@ -1985,7 +1992,7 @@ const stealResource = (game, session, color) => {
addChatMessage(game, session, addChatMessage(game, session,
`${session.name} randomly stole 1 ${type} from ` + `${session.name} randomly stole 1 ${type} from ` +
`${victim.name}.`); `${victim.name}.`);
sendUpdateToPlayer(victim, { sendUpdateToPlayer(game, victim, {
private: victim.player private: victim.player
}); });
} }
@ -1993,7 +2000,7 @@ const stealResource = (game, session, color) => {
game.turn.robberInAction = false; game.turn.robberInAction = false;
sendUpdateToPlayer(session, { sendUpdateToPlayer(game, session, {
private: session.player private: session.player
}); });
sendUpdateToPlayers(game, { sendUpdateToPlayers(game, {
@ -2052,7 +2059,7 @@ const buyDevelopment = (game, session) => {
activities: game.activities, activities: game.activities,
players: getFilteredPlayers(game) players: getFilteredPlayers(game)
}); });
sendUpdateToPlayer(session, { sendUpdateToPlayer(game, session, {
private: session.player private: session.player
}); });
} }
@ -2166,7 +2173,7 @@ const playCard = (game, session, card) => {
} }
} }
sendUpdateToPlayer(session, { sendUpdateToPlayer(game, session, {
private: session.player private: session.player
}); });
sendUpdateToPlayers(game, { sendUpdateToPlayers(game, {
@ -2296,15 +2303,15 @@ const placeSettlement = (game, session, index) => {
setForRoadPlacement(game, layout.corners[index].roads); setForRoadPlacement(game, layout.corners[index].roads);
} }
sendUpdateToPlayer(game, session, {
private: session.player
});
sendUpdateToPlayers(game, { sendUpdateToPlayers(game, {
placements: game.placements, placements: game.placements,
activities: game.activities, activities: game.activities,
turn: game.turn, turn: game.turn,
chat: game.chat chat: game.chat
}); });
sendUpdateToPlayer(session, {
private: session.player
});
} }
const placeRoad = (game, session, index) => { const placeRoad = (game, session, index) => {
@ -2392,25 +2399,27 @@ const placeRoad = (game, session, index) => {
let next; let next;
if (game.direction === 'forward' && getLastPlayerName(game) === session.name) { if (game.direction === 'forward' && getLastPlayerName(game) === session.name) {
game.direction = 'backward'; game.direction = 'backward';
next = session.name; next = session.player;
} else if (game.direction === 'backward' && getFirstPlayerName(game) === session.name) { } else if (game.direction === 'backward' && getFirstPlayerName(game) === session.name) {
/* Done! */ /* Done! */
delete game.direction; delete game.direction;
} else { } else {
if (game.direction === 'forward') { if (game.direction === 'forward') {
next = getNextPlayer(game, session.name); next = getNextPlayer(game, session.name);
console.log(`advance from ${session.name} to `, next);
} else { } else {
next = getPrevPlayer(game, session.name); next = getPrevPlayer(game, session.name);
console.log(`devance from ${session.name} to `, next);
} }
} }
if (next) { if (next) {
game.turn = { game.turn = {
name: next, name: next.name,
color: getColorFromName(game, next) color: next.color
}; };
setForSettlementPlacement(game, getValidCorners(game)); setForSettlementPlacement(game, getValidCorners(game));
calculateRoadLengths(game, session); calculateRoadLengths(game, session);
addChatMessage(game, null, `It is ${next}'s turn to place a settlement.`); addChatMessage(game, null, `It is ${next.name}'s turn to place a settlement.`);
} else { } else {
game.turn = { game.turn = {
actions: [], actions: [],
@ -2418,6 +2427,7 @@ const placeRoad = (game, session, index) => {
name: session.name, name: session.name,
color: getColorFromName(game, session.name) color: getColorFromName(game, session.name)
}; };
session.player.startTime = Date.now();
addChatMessage(game, null, `Everyone has placed their two settlements!`); addChatMessage(game, null, `Everyone has placed their two settlements!`);
@ -2442,6 +2452,9 @@ const placeRoad = (game, session, index) => {
for (let type in receives) { for (let type in receives) {
player[type] += receives[type]; player[type] += receives[type];
player.resources += receives[type]; player.resources += receives[type];
sendUpdateToPlayer(game, session, {
private: player
});
message.push(`${receives[type]} ${type}`); message.push(`${receives[type]} ${type}`);
} }
addChatMessage(game, session, `${session.name} receives ${message.join(', ')}.`); addChatMessage(game, session, `${session.name} receives ${message.join(', ')}.`);
@ -2451,7 +2464,7 @@ const placeRoad = (game, session, index) => {
game.state = 'normal'; game.state = 'normal';
} }
} }
sendUpdateToPlayer(session, { sendUpdateToPlayer(game, session, {
private: session.player private: session.player
}); });
sendUpdateToPlayers(game, { sendUpdateToPlayers(game, {
@ -2511,7 +2524,7 @@ const discard = (game, session, discards) => {
game.turn.limits.pips.push(i); game.turn.limits.pips.push(i);
} }
} }
sendUpdateToPlayer(session, { sendUpdateToPlayer(game, session, {
private: player private: player
}); });
sendUpdateToPlayers(game, { sendUpdateToPlayers(game, {
@ -2624,7 +2637,7 @@ const selectResources = (game, session, cards) => {
player[type] = 0; player[type] = 0;
for (let key in game.sessions) { for (let key in game.sessions) {
if (game.sessions[key].player === player) { if (game.sessions[key].player === player) {
sendUpdateToPlayer(game.sessions[key], { sendUpdateToPlayer(game, game.sessions[key], {
private: game.sessions[key].player private: game.sessions[key].player
}); });
break; break;
@ -2652,7 +2665,7 @@ const selectResources = (game, session, cards) => {
} }
delete game.turn.active; delete game.turn.active;
game.turn.actions = []; game.turn.actions = [];
sendUpdateToPlayer(session, { sendUpdateToPlayer(game, session, {
private: session.player private: session.player
}); });
sendUpdateToPlayers(game, { sendUpdateToPlayers(game, {
@ -2786,7 +2799,7 @@ const placeCity = (game, session, index) => {
game.turn.actions = []; game.turn.actions = [];
game.turn.limits = {}; game.turn.limits = {};
addActivity(game, session, `${session.name} upgraded a settlement to a city!`); addActivity(game, session, `${session.name} upgraded a settlement to a city!`);
sendUpdateToPlayer(session, { sendUpdateToPlayer(game, session, {
private: session.player private: session.player
}); });
sendUpdateToPlayers(game, { sendUpdateToPlayers(game, {
@ -3028,6 +3041,8 @@ const sendUpdateToPlayers = async (game, update) => {
} }
} }
calculatePoints(game, update);
if (debug.update) { if (debug.update) {
console.log(`[ all ]: -> sendUpdateToPlayers - `, update); console.log(`[ all ]: -> sendUpdateToPlayers - `, update);
} else { } else {
@ -3035,7 +3050,6 @@ const sendUpdateToPlayers = async (game, update) => {
console.log(`[ all ]: -> sendUpdateToPlayers - ${keys.join(',')}`); console.log(`[ all ]: -> sendUpdateToPlayers - ${keys.join(',')}`);
} }
const message = JSON.stringify({ const message = JSON.stringify({
type: 'game-update', type: 'game-update',
update update
@ -3050,7 +3064,7 @@ const sendUpdateToPlayers = async (game, update) => {
await saveGame(game); await saveGame(game);
} }
const sendUpdateToPlayer = async (session, update) => { const sendUpdateToPlayer = async (game, session, update) => {
/* Ensure clearing of a field actually gets sent by setting /* Ensure clearing of a field actually gets sent by setting
* undefined to 'false' * undefined to 'false'
@ -3061,6 +3075,8 @@ const sendUpdateToPlayer = async (session, update) => {
} }
} }
calculatePoints(game, update);
if (debug.update) { if (debug.update) {
console.log(`${session.id}: -> sendUpdateToPlayer:${getName(session)} - `, update); console.log(`${session.id}: -> sendUpdateToPlayer:${getName(session)} - `, update);
} else { } else {
@ -3136,6 +3152,123 @@ const getFilteredPlayers = (game) => {
return filtered; return filtered;
}; };
const calculatePoints = (game, update) => {
if (game.state === 'winner') {
return;
}
/* Calculate points and determine if there is a winner */
for (let key in game.players) {
const player = game.players[key];
if (player.status === 'Not active') {
continue;
}
player.points = 0;
if (key === game.longestRoad) {
player.points += 2;
}
if (key === game.largestArmy) {
player.points += 2;
}
player.points += MAX_SETTLEMENTS - player.settlements;
player.points += 2 * (MAX_CITIES - player.cities);
player.unplayed = 0;
player.potential = 0;
player.development.forEach(card => {
if (card.type === 'vp') {
if (card.played) {
player.points++;
} else {
player.potential++;
}
}
if (!card.played) {
player.unplayed++;
}
});
/* This player has 10 points! Check if they are the current
* player and if so, declare victory! */
if (player.points >= 10) {
console.log(`${info}: Whoa! ${player.name} has ${player.points}!`);
for (let key in game.sessions) {
if (game.sessions[key].color !== player.color
|| game.sessions[key].status === 'Not active') {
continue;
}
const message = `Wahoo! ${player.name} has 10 points on their turn and has won!`;
addChatMessage(game, null, message)
console.log(`${info}: ${message}`);
update.winner = Object.assign({}, player, {
state: 'winner',
robberStole: game.robberStole,
stolen: game.stolen,
chat: game.chat,
turns: game.turns,
players: game.players,
elapsedTime: Date.now() - game.startTime
});
game.winner = update.winner;
game.state = 'winner';
game.waiting = [];
}
}
}
/* If the game isn't in a win state, do not share development card information
* with other players */
if (game.state !== 'winner') {
for (let key in game.players) {
const player = game.players[key];
if (player.status === 'Not active') {
continue;
}
delete player.potential;
}
}
}
const gotoLobby = (game, session) => {
if (!game.waiting) {
game.waiting = [];
}
const already = game.waiting.indexOf(session.name) !== -1;
const waitingFor = [];
for (let key in game.sessions) {
if (game.sessions[key] === session) {
continue;
}
if (game.sessions[key].player && game.waiting.indexOf(game.sessions[key].name) == -1) {
waitingFor.push(game.sessions[key].name);
}
}
if (!already) {
game.waiting.push(session.name);
addChatMessage(game, null, `${session.name} has gone to the lobby.`);
} else if (waitingFor.length !== 0) {
return `You are already waiting in the lobby. ` +
`${waitingFor.join(',')} still needs to go to the lobby.`;
}
if (waitingFor.length === 0) {
resetGame(game);
addChatMessage(game, null, `All players are back to the lobby.`);
addChatMessage(game, null,
`The game has been reset. You can play again with this board, or `+
`click 'New board' to mix things up a bit.`);
sendGameToPlayers(game);
return;
}
addChatMessage(game, null, `Waiting for ${waitingFor.join(',')} to go to lobby.`);
sendUpdateToPlayers(game, {
chat: game.chat
});
}
router.ws("/ws/:id", async (ws, req) => { router.ws("/ws/:id", async (ws, req) => {
if (!req.cookies || !req.cookies.player) { if (!req.cookies || !req.cookies.player) {
ws.send({ type: 'error', error: `Unable to set session cookie` }); ws.send({ type: 'error', error: `Unable to set session cookie` });
@ -3361,7 +3494,7 @@ router.ws("/ws/:id", async (ws, req) => {
break; break;
} }
}); });
sendUpdateToPlayer(session, update); sendUpdateToPlayer(game, session, update);
break; break;
case 'chat': case 'chat':
@ -3501,7 +3634,15 @@ router.ws("/ws/:id", async (ws, req) => {
if (warning) { if (warning) {
sendWarning(session, warning); sendWarning(session, warning);
} else { } else {
sendUpdateToPlayer(session, { for (let key in game.sessions) {
const tmp = game.sessions[key];
if (tmp.player) {
sendUpdateToPlayer(game, tmp, {
private: tmp.player
});
}
}
sendUpdateToPlayer(game, session, {
private: session.player private: session.player
}); });
sendUpdateToPlayers(game, { sendUpdateToPlayers(game, {
@ -3512,6 +3653,13 @@ router.ws("/ws/:id", async (ws, req) => {
}); });
} }
break; break;
case 'goto-lobby':
console.log(`${short}: <- goto-lobby:${getName(session)}`);
warning = gotoLobby(game, session);
if (warning) {
sendWarning(session, warning);
}
break;
default: default:
console.warn(`Unsupported request: ${data.type}`); console.warn(`Unsupported request: ${data.type}`);
break; break;
@ -3593,56 +3741,6 @@ const debugChat = (game, preamble) => {
} }
const getFilteredGameForPlayer = (game, session) => { const getFilteredGameForPlayer = (game, session) => {
/* Calculate points and determine if there is a winner */
for (let key in game.players) {
const player = game.players[key];
if (player.status === 'Not active') {
continue;
}
player.points = 0;
if (key === game.longestRoad) {
player.points += 2;
}
if (key === game.largestArmy) {
player.points += 2;
}
player.points += MAX_SETTLEMENTS - player.settlements;
player.points += 2 * (MAX_CITIES - player.cities);
player.unplayed = 0;
player.potential = 0;
player.development.forEach(card => {
if (card.type === 'vp') {
if (card.played) {
player.points++;
} else {
player.potential++;
}
}
if (!card.played) {
player.unplayed++;
}
});
console.log(`${todo}: Move game win state to card play section`);
if (!game.winner && (player.points >= 10 && session.color === key)) {
game.winner = key;
game.state = 'winner';
delete game.turn.roll;
}
}
/* If the game isn't in a win state, do not share development card information
* with other players */
if (game.state !== 'winner') {
for (let key in game.players) {
const player = game.players[key];
if (player.status === 'Not active') {
continue;
}
delete player.potential;
}
}
/* Shallow copy game, filling its sessions with a shallow copy of /* Shallow copy game, filling its sessions with a shallow copy of
* sessions so we can then delete the player field from them */ * sessions so we can then delete the player field from them */
@ -3686,7 +3784,7 @@ const getFilteredGameForPlayer = (game, session) => {
}); });
} }
const robberSteal = (game, color, type) => { const robberSteal = (game, color, type, count) => {
if (!game.stolen) { if (!game.stolen) {
game.stolen = {}; game.stolen = {};
} }
@ -3699,9 +3797,9 @@ const robberSteal = (game, color, type) => {
if (!(type in game.stolen[color])) { if (!(type in game.stolen[color])) {
game.stolen[color][type] = 0; game.stolen[color][type] = 0;
} }
game.robberStole = game.robberStole ? game.robberStole++ : 1; game.robberStole = game.robberStole += count;
game.stolen[type]++; game.stolen[type] += count;
game.stolen[color][type]++; game.stolen[color][type] += count;
} }
const resetGame = (game) => { const resetGame = (game) => {
@ -3728,6 +3826,7 @@ const resetGame = (game) => {
signature: game.signature, signature: game.signature,
players: game.players, players: game.players,
stolen: {}, stolen: {},
robberStole: 0,
longestRoad: '', longestRoad: '',
longestRoadLength: 0, longestRoadLength: 0,
largestArmy: '', largestArmy: '',
@ -3793,6 +3892,8 @@ const resetGame = (game) => {
session.player.status = 'Active'; session.player.status = 'Active';
session.player.lastActive = Date.now(); session.player.lastActive = Date.now();
session.player.live = session.live; session.player.live = session.live;
session.player.name = session.name;
session.player.color = session.color;
} }
} }
} }
@ -3838,20 +3939,6 @@ const createGame = (id) => {
return game; return game;
}; };
router.post("/", (req, res/*, next*/) => {
console.log("POST games/");
const game = createGame();
if (!req.session.player_id) {
req.session.player_id = crypto.randomBytes(16).toString('hex');
console.log(`[${req.session.player_id.substring(0, 8)}]: https - New session connected`);
} else {
console.log(`[${req.session.player_id.substring(0, 8)}]: https - Existing session being used`);
}
const session = getSession(game, req.cookies.player);
saveGame(game);
return res.status(200).send(getFilteredGameForPlayer(game, session));
});
const setBeginnerGame = (game) => { const setBeginnerGame = (game) => {
pickRobber(game); pickRobber(game);
shuffleArray(game.developmentCards); shuffleArray(game.developmentCards);