Board rendering only occurs once per signature
Signed-off-by: James Ketrenos <james_eikona@ketrenos.com>
This commit is contained in:
parent
dfc5123d25
commit
222ca2d3c3
@ -28,7 +28,7 @@ body {
|
|||||||
left: 0;
|
left: 0;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
background-color: #00000060;
|
background-color: #80000060;
|
||||||
}
|
}
|
||||||
|
|
||||||
.Table .ErrorDialog .Error {
|
.Table .ErrorDialog .Error {
|
||||||
@ -36,6 +36,23 @@ body {
|
|||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.Table .WarningDialog {
|
||||||
|
z-index: 10000;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-around;
|
||||||
|
align-items: center;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.Table .WarningDialog .Warning {
|
||||||
|
display: flex;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.Table .Game {
|
.Table .Game {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -17,7 +17,8 @@ import { Chat } from "./Chat.js";
|
|||||||
import { MediaAgent } from "./MediaControl.js";
|
import { MediaAgent } from "./MediaControl.js";
|
||||||
import { Board } from "./Board.js";
|
import { Board } from "./Board.js";
|
||||||
import { Actions } from "./Actions.js";
|
import { Actions } from "./Actions.js";
|
||||||
import { base, gamesPath, debounce } from './Common.js';
|
import { base, gamesPath } from './Common.js';
|
||||||
|
import { GameOrder } from "./GameOrder.js";
|
||||||
import history from "./history.js";
|
import history from "./history.js";
|
||||||
import "./App.css";
|
import "./App.css";
|
||||||
|
|
||||||
@ -28,66 +29,89 @@ const Table = () => {
|
|||||||
const [ ws, setWs ] = useState(global.ws);
|
const [ ws, setWs ] = useState(global.ws);
|
||||||
const [ name, setName ] = useState(global.name);
|
const [ name, setName ] = useState(global.name);
|
||||||
const [ error, setError ] = useState(undefined);
|
const [ error, setError ] = useState(undefined);
|
||||||
|
const [ warning, setWarning ] = useState(undefined);
|
||||||
const [ peers, setPeers ] = useState({});
|
const [ peers, setPeers ] = useState({});
|
||||||
const [loaded, setLoaded] = useState(false);
|
const [loaded, setLoaded] = useState(false);
|
||||||
const [connecting, setConnecting] = useState(false);
|
const [connecting, setConnecting] = useState(undefined);
|
||||||
|
const [state, setState] = useState(undefined);
|
||||||
|
const [color, setColor] = useState(undefined);
|
||||||
|
const fields = [ 'name', 'id', 'state', 'color', 'name' ];
|
||||||
|
|
||||||
|
/*
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log(peers);
|
console.log(peers);
|
||||||
}, [peers]);
|
}, [peers]);
|
||||||
|
*/
|
||||||
|
|
||||||
const onWsOpen = (event) => {
|
const onWsOpen = (event) => {
|
||||||
console.log(`ws: open`);
|
console.log(`ws: open`);
|
||||||
setError("");
|
setError("");
|
||||||
|
|
||||||
/* We do not set the socket as bound until the 'open' message
|
/* We do not set the socket as connected until the 'open' message
|
||||||
* comes through */
|
* comes through */
|
||||||
setWs(event.target);
|
setConnecting(event.target);
|
||||||
setConnecting(false);
|
|
||||||
|
|
||||||
/* Request a full game-update
|
/* Request a full game-update
|
||||||
* We only need gameId and name for App.js, however in the event
|
* We only need gameId and name for App.js, however in the event
|
||||||
* of a network disconnect, we need to refresh the entire game
|
* of a network disconnect, we need to refresh the entire game
|
||||||
* state on reload so all bound components reflect the latest
|
* state on reload so all bound components reflect the latest
|
||||||
* state */
|
* state */
|
||||||
if (loaded) {
|
event.target.send(JSON.stringify({
|
||||||
event.target.send(JSON.stringify({
|
type: 'game-update'
|
||||||
type: 'game-update'
|
}));
|
||||||
}));
|
event.target.send(JSON.stringify({
|
||||||
if (name) {
|
type: 'get',
|
||||||
event.target.send(JSON.stringify({
|
fields
|
||||||
type: 'player-name',
|
}));
|
||||||
name
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
event.target.send(JSON.stringify({
|
|
||||||
type: 'get',
|
|
||||||
fields: [ 'name', 'id' ]
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onWsMessage = (event) => {
|
const onWsMessage = (event) => {
|
||||||
const data = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
case 'error':
|
case 'error':
|
||||||
console.error(data.error);
|
console.error(`App - error`, data.error);
|
||||||
window.alert(data.error);
|
window.alert(data.error);
|
||||||
break;
|
break;
|
||||||
|
case 'warning':
|
||||||
|
setWarning(`App - warning`, data.warning);
|
||||||
|
setTimeout(() => {
|
||||||
|
if (data.warning === warning) {
|
||||||
|
setWarning("");
|
||||||
|
}
|
||||||
|
}, 3000);
|
||||||
|
break;
|
||||||
case 'game-update':
|
case 'game-update':
|
||||||
if (!loaded) {
|
if (!loaded) {
|
||||||
setLoaded(true);
|
setLoaded(true);
|
||||||
}
|
}
|
||||||
console.log(`ws: message - ${data.type}`, data.update);
|
console.log(`ws: message - ${data.type}`, data.update);
|
||||||
|
if ('player' in data.update) {
|
||||||
|
const player = data.update.player;
|
||||||
|
if (player.name !== name) {
|
||||||
|
console.log(`App - setting name (via player): ${data.update.name}`);
|
||||||
|
setName(data.update.name);
|
||||||
|
}
|
||||||
|
if (player.color !== color) {
|
||||||
|
console.log(`App - setting color (via player): ${data.update.color}`);
|
||||||
|
setColor(data.update.color);
|
||||||
|
}
|
||||||
|
}
|
||||||
if ('name' in data.update && data.update.name !== name) {
|
if ('name' in data.update && data.update.name !== name) {
|
||||||
console.log(`Updating name to ${data.update.name}`);
|
console.log(`App - setting name: ${data.update.name}`);
|
||||||
setName(data.update.name);
|
setName(data.update.name);
|
||||||
}
|
}
|
||||||
if ('id' in data.update && data.update.id !== gameId) {
|
if ('id' in data.update && data.update.id !== gameId) {
|
||||||
console.log(`Updating id to ${data.update.id}`);
|
console.log(`App - setting gameId ${data.update.id}`);
|
||||||
setGameId(data.update.id);
|
setGameId(data.update.id);
|
||||||
}
|
}
|
||||||
|
if ('state' in data.update && data.update.state !== state) {
|
||||||
|
console.log(`App - setting game state: ${data.update.state}`);
|
||||||
|
setState(data.update.state);
|
||||||
|
}
|
||||||
|
if ('color' in data.update && data.update.color !== color) {
|
||||||
|
console.log(`App - setting color: ${color}`);
|
||||||
|
setColor(data.update.color);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -98,7 +122,7 @@ const Table = () => {
|
|||||||
let timer = 0;
|
let timer = 0;
|
||||||
function reset() {
|
function reset() {
|
||||||
timer = 0;
|
timer = 0;
|
||||||
setConnecting(false);
|
setConnecting(undefined);
|
||||||
};
|
};
|
||||||
return _ => {
|
return _ => {
|
||||||
if (timer) {
|
if (timer) {
|
||||||
@ -109,20 +133,19 @@ const Table = () => {
|
|||||||
})();
|
})();
|
||||||
|
|
||||||
const onWsError = (event) => {
|
const onWsError = (event) => {
|
||||||
console.log(`ws: error`, event);
|
console.error(`ws: error`, event);
|
||||||
const error = `Connection to Ketr Ketran game server failed! ` +
|
const error = `Connection to Ketr Ketran game server failed! ` +
|
||||||
`Connection attempt will be retried every 5 seconds.`;
|
`Connection attempt will be retried every 5 seconds.`;
|
||||||
console.error(error);
|
setError(error);
|
||||||
setWs(undefined);
|
setWs(undefined);
|
||||||
resetConnection();
|
resetConnection();
|
||||||
setError(error);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const onWsClose = (event) => {
|
const onWsClose = (event) => {
|
||||||
const error = `Connection to Ketr Ketran game was lost. ` +
|
const error = `Connection to Ketr Ketran game was lost. ` +
|
||||||
`Attempting to reconnect...`;
|
`Attempting to reconnect...`;
|
||||||
console.error(error);
|
console.warn(`ws: close`);
|
||||||
console.log(`ws: close`);
|
setError(error);
|
||||||
setWs(undefined);
|
setWs(undefined);
|
||||||
resetConnection();
|
resetConnection();
|
||||||
};
|
};
|
||||||
@ -152,7 +175,7 @@ const Table = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("Requesting new game.");
|
console.log(`Requesting new game.`);
|
||||||
|
|
||||||
window.fetch(`${base}/api/v1/games/`, {
|
window.fetch(`${base}/api/v1/games/`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@ -190,8 +213,13 @@ const Table = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let socket = ws;
|
const unbind = () => {
|
||||||
if (!ws) {
|
console.log(`table - unbind`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`table - bind`);
|
||||||
|
|
||||||
|
if (!ws && !connecting) {
|
||||||
let loc = window.location, new_uri;
|
let loc = window.location, new_uri;
|
||||||
if (loc.protocol === "https:") {
|
if (loc.protocol === "https:") {
|
||||||
new_uri = "wss";
|
new_uri = "wss";
|
||||||
@ -200,42 +228,68 @@ const Table = () => {
|
|||||||
}
|
}
|
||||||
new_uri = `${new_uri}://${loc.host}${base}/api/v1/games/ws/${gameId}`;
|
new_uri = `${new_uri}://${loc.host}${base}/api/v1/games/ws/${gameId}`;
|
||||||
console.log(`Attempting WebSocket connection to ${new_uri}`);
|
console.log(`Attempting WebSocket connection to ${new_uri}`);
|
||||||
socket = new WebSocket(new_uri);
|
setWs(new WebSocket(new_uri));
|
||||||
setConnecting(true);
|
setConnecting(undefined);
|
||||||
|
return unbind;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ws) {
|
||||||
|
return unbind;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('table - bind');
|
|
||||||
const cbOpen = e => refWsOpen.current(e);
|
const cbOpen = e => refWsOpen.current(e);
|
||||||
const cbMessage = e => refWsMessage.current(e);
|
const cbMessage = e => refWsMessage.current(e);
|
||||||
const cbClose = e => refWsClose.current(e);
|
const cbClose = e => refWsClose.current(e);
|
||||||
const cbError = e => refWsError.current(e);
|
const cbError = e => refWsError.current(e);
|
||||||
|
|
||||||
socket.addEventListener('open', cbOpen);
|
ws.addEventListener('open', cbOpen);
|
||||||
socket.addEventListener('close', cbClose);
|
ws.addEventListener('close', cbClose);
|
||||||
socket.addEventListener('error', cbError);
|
ws.addEventListener('error', cbError);
|
||||||
socket.addEventListener('message', cbMessage);
|
ws.addEventListener('message', cbMessage);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
if (socket) {
|
unbind();
|
||||||
console.log('table - unbind');
|
ws.removeEventListener('open', cbOpen);
|
||||||
socket.removeEventListener('open', cbOpen);
|
ws.removeEventListener('close', cbClose);
|
||||||
socket.removeEventListener('close', cbClose);
|
ws.removeEventListener('error', cbError);
|
||||||
socket.removeEventListener('error', cbError);
|
ws.removeEventListener('message', cbMessage);
|
||||||
socket.removeEventListener('message', cbMessage);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, [ setWs, connecting, setConnecting, gameId, ws, refWsOpen, refWsMessage, refWsClose, refWsError ]);
|
}, [ setWs, connecting, setConnecting, gameId, ws, refWsOpen, refWsMessage, refWsClose, refWsError ]);
|
||||||
|
|
||||||
console.log(`Loaded: ${loaded}`);
|
return <GlobalContext.Provider value={{ ws: connecting, name, gameId, peers, setPeers }}>
|
||||||
|
|
||||||
return <GlobalContext.Provider value={{
|
|
||||||
ws, name, gameId, peers, setPeers }}>
|
|
||||||
<MediaAgent/>
|
<MediaAgent/>
|
||||||
<PingPong/>
|
<PingPong/>
|
||||||
<div className="Table">
|
<div className="Table">
|
||||||
{ error && <div className="ErrorDialog"><Paper className="Error">{ error }</Paper></div> }
|
|
||||||
<div className="Game">
|
<div className="Game">
|
||||||
|
{ error && <div className="ErrorDialog"><Paper className="Error">{ error }</Paper></div> }
|
||||||
|
{ warning && <div className="WarningDialog"><Paper className="Warning">{ warning }</Paper></div> }
|
||||||
<Board/>
|
<Board/>
|
||||||
|
{ color && state === 'game-order' &&
|
||||||
|
<GameOrder/>
|
||||||
|
}
|
||||||
|
|
||||||
|
{ /* state === 'winner' &&
|
||||||
|
<Winner color={winner}/>
|
||||||
|
}
|
||||||
|
|
||||||
|
{ state === 'normal' &&
|
||||||
|
turn && turn.actions && turn.actions.indexOf('trade') !== -1 &&
|
||||||
|
<Trade/>
|
||||||
|
}
|
||||||
|
|
||||||
|
{ cardActive &&
|
||||||
|
<ViewCard card={cardActive}/>
|
||||||
|
}
|
||||||
|
|
||||||
|
{ isTurn && turn && turn.actions && game.turn.actions.indexOf('select-resources') !== -1 &&
|
||||||
|
<ChooseCard type={turn.active}/>
|
||||||
|
}
|
||||||
|
|
||||||
|
{ game.state === 'normal' &&
|
||||||
|
turn && isTurn && turn.actions && turn.actions.indexOf('steal-resource') !== -1 &&
|
||||||
|
<SelectPlayer table={this} game={this.state} players={game.turn.limits.players}/>
|
||||||
|
*/ }
|
||||||
|
|
||||||
<div className="Hand">todo: player's hand</div>
|
<div className="Hand">todo: player's hand</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="Sidebar">
|
<div className="Sidebar">
|
||||||
@ -248,7 +302,6 @@ const Table = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
console.log(`Base: ${base}`);
|
|
||||||
return (
|
return (
|
||||||
<Router>
|
<Router>
|
||||||
<Routes>
|
<Routes>
|
||||||
|
@ -2,6 +2,24 @@ import React, { useEffect, useState, useContext, useRef, useMemo } from "react";
|
|||||||
import { assetsPath, debounce } from "./Common.js";
|
import { assetsPath, debounce } from "./Common.js";
|
||||||
import "./Board.css";
|
import "./Board.css";
|
||||||
import { GlobalContext } from "./GlobalContext.js";
|
import { GlobalContext } from "./GlobalContext.js";
|
||||||
|
const rows = [3, 4, 5, 4, 3, 2]; /* The final row of 2 is to place roads and corners */
|
||||||
|
|
||||||
|
const
|
||||||
|
hexRatio = 1.1547,
|
||||||
|
tileWidth = 67,
|
||||||
|
tileHalfWidth = tileWidth * 0.5,
|
||||||
|
tileHeight = tileWidth * hexRatio,
|
||||||
|
tileHalfHeight = tileHeight * 0.5,
|
||||||
|
radius = tileHeight * 2,
|
||||||
|
borderOffsetX = 86, /* ~1/10th border image width... hand tuned */
|
||||||
|
borderOffsetY = 3;
|
||||||
|
|
||||||
|
/* Actual sizing */
|
||||||
|
const
|
||||||
|
tileImageWidth = 90, /* Based on hand tuned and image width */
|
||||||
|
tileImageHeight = tileImageWidth/hexRatio,
|
||||||
|
borderImageWidth = (2 + 2/3) * tileImageWidth, /* 2.667 * .Tile.width */
|
||||||
|
borderImageHeight = borderImageWidth * 0.29; /* 0.29 * .Border.height */
|
||||||
|
|
||||||
const Board = () => {
|
const Board = () => {
|
||||||
const { ws } = useContext(GlobalContext);
|
const { ws } = useContext(GlobalContext);
|
||||||
@ -12,13 +30,14 @@ const Board = () => {
|
|||||||
const [cornerElements, setCornerElements] = useState(<></>);
|
const [cornerElements, setCornerElements] = useState(<></>);
|
||||||
const [roadElements, setRoadElements] = useState(<></>);
|
const [roadElements, setRoadElements] = useState(<></>);
|
||||||
const [ signature, setSignature ] = useState("");
|
const [ signature, setSignature ] = useState("");
|
||||||
|
const [ generated, setGenerated ] = useState("");
|
||||||
const [ robber, setRobber ] = useState(-1);
|
const [ robber, setRobber ] = useState(-1);
|
||||||
const [ robberName, setRobberName ] = useState([]);
|
const [ robberName, setRobberName ] = useState([]);
|
||||||
const [ pips, setPips ] = useState([]);
|
const [ pips, setPips ] = useState();
|
||||||
const [ pipOrder, setPipOrder ] = useState([]);
|
const [ pipOrder, setPipOrder ] = useState();
|
||||||
const [ borders, setBorders ] = useState([]);
|
const [ borders, setBorders ] = useState();
|
||||||
const [ borderOrder, setBorderOrder ] = useState([]);
|
const [ borderOrder, setBorderOrder ] = useState();
|
||||||
const [ tiles, setTiles ] = useState([]);
|
const [ tiles, setTiles ] = useState();
|
||||||
const [ tileOrder, setTileOrder ] = useState([]);
|
const [ tileOrder, setTileOrder ] = useState([]);
|
||||||
const [ placements, setPlacements ] = useState(undefined);
|
const [ placements, setPlacements ] = useState(undefined);
|
||||||
const [ turn, setTurn ] = useState({});
|
const [ turn, setTurn ] = useState({});
|
||||||
@ -31,59 +50,120 @@ const Board = () => {
|
|||||||
'placements', 'turn', 'state', 'color', 'longestRoadLength'
|
'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 onWsMessage = (event) => {
|
||||||
const data = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
case 'game-update':
|
case 'game-update':
|
||||||
if (data.update.signature && data.update.signature !== signature) {
|
console.log(`board - game update`, data.update);
|
||||||
setSignature(data.update.signature);
|
if ('robber' in data.update && data.update.robber !== robber) {
|
||||||
}
|
|
||||||
|
|
||||||
if (data.update.robber && data.update.robber !== robber) {
|
|
||||||
setRobber(data.update.robber);
|
setRobber(data.update.robber);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.update.robberName && data.update.robberName !== robberName) {
|
if ('robberName' in data.update && data.update.robberName !== robberName) {
|
||||||
setRobberName(data.update.robberName);
|
setRobberName(data.update.robberName);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.update.pips) {
|
if ('state' in data.update && data.update.state !== state) {
|
||||||
setPips(data.update.pips);
|
|
||||||
}
|
|
||||||
if (data.update.pipOrder) {
|
|
||||||
setPipOrder(data.update.pipOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.update.borders) {
|
|
||||||
setBorders(data.update.borders);
|
|
||||||
}
|
|
||||||
if (data.update.borderOrder) {
|
|
||||||
setBorderOrder(data.update.borderOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.update.tiles) {
|
|
||||||
setTiles(data.update.tiles);
|
|
||||||
}
|
|
||||||
if (data.update.tileOrder) {
|
|
||||||
setTileOrder(data.update.tileOrder);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.update.state && data.update.state !== state) {
|
|
||||||
setState(data.update.state);
|
setState(data.update.state);
|
||||||
}
|
}
|
||||||
if (data.update.color && data.update.color !== color) {
|
|
||||||
|
if ('color' in data.update && data.update.color !== color) {
|
||||||
setColor(data.update.color);
|
setColor(data.update.color);
|
||||||
}
|
}
|
||||||
if (data.update.longestRoadLength
|
|
||||||
|
if ('longestRoadLength' in data.update
|
||||||
&& data.update.longestRoadLength !== longestRoadLength) {
|
&& data.update.longestRoadLength !== longestRoadLength) {
|
||||||
setLongestRoadLength(data.update.longestRoadLength);
|
setLongestRoadLength(data.update.longestRoadLength);
|
||||||
}
|
}
|
||||||
if (data.update.turn) {
|
|
||||||
|
if ('turn' in data.update) {
|
||||||
setTurn(data.update.turn);
|
setTurn(data.update.turn);
|
||||||
}
|
}
|
||||||
if (data.update.placements) {
|
|
||||||
console.log(`placements`, data.update.placements);
|
if ('placement' in data.update) {
|
||||||
setPlacements(data.update.placements);
|
if (comparePlacements(data.update.placements, placements)) {
|
||||||
|
console.log(`placements`, data.update.placements);
|
||||||
|
setPlacements(data.update.placements);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('signature' in data.update && data.update.signature !== signature) {
|
||||||
|
setSignature(data.update.signature);
|
||||||
|
|
||||||
|
/* The following are only updated if there is a new game
|
||||||
|
* signature */
|
||||||
|
|
||||||
|
if ('pipOrder' in data.update) {
|
||||||
|
setPipOrder(data.update.pipOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('borderOrder' in data.update) {
|
||||||
|
setBorderOrder(data.update.borderOrder);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('tileOrder' in data.update) {
|
||||||
|
setTileOrder(data.update.tileOrder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This is permanent static data from the server -- do not update
|
||||||
|
* once set */
|
||||||
|
if ('pips' in data.update && !pips) {
|
||||||
|
setPips(data.update.pips);
|
||||||
|
}
|
||||||
|
if ('tiles' in data.update && !tiles) {
|
||||||
|
setTiles(data.update.tiles);
|
||||||
|
}
|
||||||
|
if ('borders' in data.update && !borders) {
|
||||||
|
setBorders(data.update.borders);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -135,9 +215,11 @@ const Board = () => {
|
|||||||
if (!ws) {
|
if (!ws) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
console.log('board - bind');
|
||||||
const cbMessage = e => refWsMessage.current(e);
|
const cbMessage = e => refWsMessage.current(e);
|
||||||
ws.addEventListener('message', cbMessage);
|
ws.addEventListener('message', cbMessage);
|
||||||
return () => {
|
return () => {
|
||||||
|
console.log('board - unbind');
|
||||||
ws.removeEventListener('message', cbMessage);
|
ws.removeEventListener('message', cbMessage);
|
||||||
}
|
}
|
||||||
}, [ws, refWsMessage]);
|
}, [ws, refWsMessage]);
|
||||||
@ -152,23 +234,6 @@ const Board = () => {
|
|||||||
}));
|
}));
|
||||||
}, [ws, fields]);
|
}, [ws, fields]);
|
||||||
|
|
||||||
const
|
|
||||||
hexRatio = 1.1547,
|
|
||||||
tileWidth = 67,
|
|
||||||
tileHalfWidth = tileWidth * 0.5,
|
|
||||||
tileHeight = tileWidth * hexRatio,
|
|
||||||
tileHalfHeight = tileHeight * 0.5,
|
|
||||||
radius = tileHeight * 2,
|
|
||||||
borderOffsetX = 86, /* ~1/10th border image width... hand tuned */
|
|
||||||
borderOffsetY = 3;
|
|
||||||
|
|
||||||
/* Actual sizing */
|
|
||||||
const
|
|
||||||
tileImageWidth = 90, /* Based on hand tuned and image width */
|
|
||||||
tileImageHeight = tileImageWidth/hexRatio,
|
|
||||||
borderImageWidth = (2 + 2/3) * tileImageWidth, /* 2.667 * .Tile.width */
|
|
||||||
borderImageHeight = borderImageWidth * 0.29; /* 0.29 * .Border.height */
|
|
||||||
|
|
||||||
const Tile = ({tile}) => {
|
const Tile = ({tile}) => {
|
||||||
const onClick = (event) => {
|
const onClick = (event) => {
|
||||||
console.log(`Tile clicked: ${tile.index}`);
|
console.log(`Tile clicked: ${tile.index}`);
|
||||||
@ -237,7 +302,7 @@ const Board = () => {
|
|||||||
sendPlacement('place-robber', pip.index);
|
sendPlacement('place-robber', pip.index);
|
||||||
};
|
};
|
||||||
|
|
||||||
return <div className="Pip"
|
return <div className="Pip"
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
data-roll={pip.roll}
|
data-roll={pip.roll}
|
||||||
data-index={pip.index}
|
data-index={pip.index}
|
||||||
@ -255,7 +320,17 @@ const Board = () => {
|
|||||||
if (!signature) {
|
if (!signature) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const rows = [3, 4, 5, 4, 3, 2]; /* The final row of 2 is to place roads and corners */
|
|
||||||
|
if (signature === generated) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pips || !pipOrder || !borders || !borderOrder
|
||||||
|
|| !tiles || !tileOrder) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`board - Generate board - ${signature}`);
|
||||||
|
|
||||||
const generateRoads = () => {
|
const generateRoads = () => {
|
||||||
let row = 0, rowCount = 0;
|
let row = 0, rowCount = 0;
|
||||||
@ -406,7 +481,6 @@ const Board = () => {
|
|||||||
return pipOrder.map(order => {
|
return pipOrder.map(order => {
|
||||||
pip = {
|
pip = {
|
||||||
roll: pips[order].roll,
|
roll: pips[order].roll,
|
||||||
robber: index === robber,
|
|
||||||
index: index++,
|
index: index++,
|
||||||
top: y,
|
top: y,
|
||||||
left: x,
|
left: x,
|
||||||
@ -484,19 +558,18 @@ const Board = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(`Generate for ${signature}`);
|
|
||||||
setPipElements(generatePips());
|
setPipElements(generatePips());
|
||||||
setBorderElements(generateBorders());
|
setBorderElements(generateBorders());
|
||||||
setTileElements(generateTiles());
|
setTileElements(generateTiles());
|
||||||
setCornerElements(generateCorners());
|
setCornerElements(generateCorners());
|
||||||
setRoadElements(generateRoads());
|
setRoadElements(generateRoads());
|
||||||
|
|
||||||
|
setGenerated(signature);
|
||||||
}, [
|
}, [
|
||||||
signature,
|
signature,
|
||||||
setPipElements, setBorderElements, setTileElements,
|
setPipElements, setBorderElements, setTileElements,
|
||||||
setCornerElements, setRoadElements,
|
setCornerElements, setRoadElements,
|
||||||
borderImageWidth, radius, tileHalfHeight, tileHalfWidth, tileHeight,
|
borderOrder, borders, pipOrder, pips, tileOrder, tiles
|
||||||
borderImageHeight,
|
|
||||||
borderOrder, borders, pipOrder, pips, robber, tileOrder, tiles
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (turn) {
|
if (turn) {
|
||||||
|
@ -1,6 +1,4 @@
|
|||||||
import React, { useState, useEffect, useContext, useRef } from "react";
|
import React, { useState, useEffect, useContext, useRef } from "react";
|
||||||
import "./Chat.css";
|
|
||||||
import PlayerColor from './PlayerColor.js';
|
|
||||||
import Paper from '@material-ui/core/Paper';
|
import Paper from '@material-ui/core/Paper';
|
||||||
import List from '@material-ui/core/List';
|
import List from '@material-ui/core/List';
|
||||||
import ListItem from '@material-ui/core/ListItem';
|
import ListItem from '@material-ui/core/ListItem';
|
||||||
@ -9,8 +7,10 @@ import Moment from 'react-moment';
|
|||||||
import TextField from '@material-ui/core/TextField';
|
import TextField from '@material-ui/core/TextField';
|
||||||
import 'moment-timezone';
|
import 'moment-timezone';
|
||||||
|
|
||||||
import Resource from './Resource.js';
|
import "./Chat.css";
|
||||||
import Dice from './Dice.js';
|
import { PlayerColor } from './PlayerColor.js';
|
||||||
|
import { Resource } from './Resource.js';
|
||||||
|
import { Dice } from './Dice.js';
|
||||||
import { GlobalContext } from "./GlobalContext.js";
|
import { GlobalContext } from "./GlobalContext.js";
|
||||||
|
|
||||||
const Chat = () => {
|
const Chat = () => {
|
||||||
|
@ -18,6 +18,6 @@ const Dice = ({ pips }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Dice;
|
export { Dice };
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,4 +14,4 @@ const PlayerColor = ({ color }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default PlayerColor;
|
export { PlayerColor };
|
@ -1,9 +1,9 @@
|
|||||||
import React, { useState, useEffect, useContext, useRef } from "react";
|
import React, { useState, useEffect, useContext, useRef } from "react";
|
||||||
import "./PlayerList.css";
|
|
||||||
import PlayerColor from './PlayerColor.js';
|
|
||||||
import Paper from '@material-ui/core/Paper';
|
import Paper from '@material-ui/core/Paper';
|
||||||
import List from '@material-ui/core/List';
|
import List from '@material-ui/core/List';
|
||||||
|
|
||||||
|
import "./PlayerList.css";
|
||||||
|
import { PlayerColor } from './PlayerColor.js';
|
||||||
import { MediaControl } from "./MediaControl.js";
|
import { MediaControl } from "./MediaControl.js";
|
||||||
import { GlobalContext } from "./GlobalContext.js";
|
import { GlobalContext } from "./GlobalContext.js";
|
||||||
|
|
||||||
@ -18,9 +18,12 @@ const PlayerList = () => {
|
|||||||
const data = JSON.parse(event.data);
|
const data = JSON.parse(event.data);
|
||||||
switch (data.type) {
|
switch (data.type) {
|
||||||
case 'game-update':
|
case 'game-update':
|
||||||
|
console.log(`player-list - game update`, data.update);
|
||||||
|
|
||||||
if ('unselected' in data.update) {
|
if ('unselected' in data.update) {
|
||||||
setUneslected(data.update.unselected);
|
setUneslected(data.update.unselected);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('players' in data.update) {
|
if ('players' in data.update) {
|
||||||
let found = false;
|
let found = false;
|
||||||
for (let key in data.update.players) {
|
for (let key in data.update.players) {
|
||||||
@ -35,6 +38,7 @@ const PlayerList = () => {
|
|||||||
}
|
}
|
||||||
setPlayers(data.update.players);
|
setPlayers(data.update.players);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ('state' in data.update && data.update.state !== state) {
|
if ('state' in data.update && data.update.state !== state) {
|
||||||
setState(data.update.state);
|
setState(data.update.state);
|
||||||
}
|
}
|
||||||
|
@ -39,4 +39,4 @@ const Resource = ({ type, select, disabled, available, count, label, onClick })
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Resource;
|
export { Resource };
|
@ -67,64 +67,6 @@ const StartButton = ({ table, game }) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const GameOrder = ({table, game}) => {
|
|
||||||
|
|
||||||
const rollClick = (event) => {
|
|
||||||
table.throwDice();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!game) {
|
|
||||||
return (<></>);
|
|
||||||
}
|
|
||||||
|
|
||||||
let players = [], hasRolled = true;
|
|
||||||
for (let color in game.players) {
|
|
||||||
const item = game.players[color],
|
|
||||||
name = getPlayerName(game.sessions, color);
|
|
||||||
if (!name) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (!item.orderRoll) {
|
|
||||||
item.orderRoll = 0;
|
|
||||||
}
|
|
||||||
if (color === game.color) {
|
|
||||||
hasRolled = item.orderRoll !== 0;
|
|
||||||
}
|
|
||||||
players.push({ name: name, color: color, ...item });
|
|
||||||
}
|
|
||||||
|
|
||||||
players.sort((A, B) => {
|
|
||||||
if (A.order === B.order) {
|
|
||||||
if (A.orderRoll === B.orderRoll) {
|
|
||||||
return A.name.localeCompare(B.name);
|
|
||||||
}
|
|
||||||
return B.orderRoll - A.orderRoll;
|
|
||||||
}
|
|
||||||
return B.order - A.order;
|
|
||||||
});
|
|
||||||
|
|
||||||
players = players.map(item =>
|
|
||||||
<div className="GameOrderPlayer" key={`player-${item.color}`}>
|
|
||||||
<PlayerColor color={item.color}/>
|
|
||||||
<div>{item.name}</div>
|
|
||||||
{ item.orderRoll !== 0 && <>rolled <Dice pips={item.orderRoll}/>. {item.orderStatus}</> }
|
|
||||||
{ item.orderRoll === 0 && <>has not rolled yet. {item.orderStatus}</>}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="GameOrder">
|
|
||||||
{ game && <Paper>
|
|
||||||
<div className="Title">Game Order</div>
|
|
||||||
<div className="PlayerList">
|
|
||||||
{ players }
|
|
||||||
</div>
|
|
||||||
<Button disabled={hasRolled} onClick={rollClick}>Roll Dice</Button>
|
|
||||||
</Paper> }
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const SelectPlayer = ({table, game, players}) => {
|
const SelectPlayer = ({table, game, players}) => {
|
||||||
const playerClick = (event) => {
|
const playerClick = (event) => {
|
||||||
table.stealResource(event.currentTarget.getAttribute('data-color'));
|
table.stealResource(event.currentTarget.getAttribute('data-color'));
|
||||||
@ -160,78 +102,6 @@ const SelectPlayer = ({table, game, players}) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const Action = ({ table, game }) => {
|
|
||||||
const buildClicked = (event) => {
|
|
||||||
table.buildClicked(event);
|
|
||||||
};
|
|
||||||
|
|
||||||
const discardClick = (event) => {
|
|
||||||
const nodes = document.querySelectorAll('.Hand .Resource.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();
|
|
||||||
};
|
|
||||||
|
|
||||||
const tradeClick = (event) => {
|
|
||||||
table.startTrading();
|
|
||||||
}
|
|
||||||
|
|
||||||
const rollClick = (event) => {
|
|
||||||
table.throwDice();
|
|
||||||
}
|
|
||||||
|
|
||||||
const passClick = (event) => {
|
|
||||||
return table.passTurn();
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
const quitClick = (event) => {
|
|
||||||
table.setSelected("");
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
if (!game.id) {
|
|
||||||
console.log("Why no game id?");
|
|
||||||
return (<Paper className="Action"/>);
|
|
||||||
}
|
|
||||||
|
|
||||||
const inLobby = game.state === 'lobby',
|
|
||||||
inGame = game.state === 'normal',
|
|
||||||
player = game ? game.player : undefined,
|
|
||||||
hasRolled = (game && game.turn && game.turn.roll) ? true : false,
|
|
||||||
isTurn = (game && game.turn && game.turn.color === game.color) ? true : false,
|
|
||||||
robberActions = (game && game.turn && game.turn.robberInAction),
|
|
||||||
haveResources = player ? player.haveResources : false,
|
|
||||||
placement = (game.state === 'initial-placement' || game.turn.active === 'road-building'),
|
|
||||||
placeRoad = placement && game.turn && game.turn.actions && game.turn.actions.indexOf('place-road') !== -1;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Paper className="Action">
|
|
||||||
{ inLobby && <>
|
|
||||||
<StartButton table={table} game={game}/>
|
|
||||||
<Button disabled={game.color ? false : true} onClick={newTableClick}>New table</Button>
|
|
||||||
<Button disabled={game.color ? true : false} onClick={() => {table.setState({ pickName: true})}}>Change name</Button> </> }
|
|
||||||
{ !inLobby && <>
|
|
||||||
<Button disabled={robberActions || !isTurn || hasRolled || !inGame} 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>
|
|
||||||
{ game.turn.roll === 7 && player && player.mustDiscard > 0 &&
|
|
||||||
<Button onClick={discardClick}>Discard</Button>
|
|
||||||
}
|
|
||||||
<Button disabled={placeRoad || robberActions || !isTurn || !hasRolled} onClick={passClick}>Done</Button>
|
|
||||||
</> }
|
|
||||||
{ /* inLobby &&
|
|
||||||
<Button onClick={quitClick}>Quit</Button>
|
|
||||||
*/ }
|
|
||||||
</Paper>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const PlayerName = ({table, game}) => {
|
const PlayerName = ({table, game}) => {
|
||||||
const [name, setName] = useState(game.name ? game.name : "");
|
const [name, setName] = useState(game.name ? game.name : "");
|
||||||
|
|
||||||
|
@ -350,6 +350,7 @@ const Trade = ({table}) => {
|
|||||||
/* Order direction is reversed for self */
|
/* Order direction is reversed for self */
|
||||||
source = {
|
source = {
|
||||||
name: item.name,
|
name: item.name,
|
||||||
|
color: item.color,
|
||||||
gets: trade.gives,
|
gets: trade.gives,
|
||||||
gives: trade.gets
|
gives: trade.gets
|
||||||
};
|
};
|
||||||
|
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user