1
0

1015 lines
29 KiB
TypeScript

/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/ban-ts-comment */
// @ts-nocheck
import React, { useEffect, useState, useContext, useRef, useMemo } from "react";
import equal from "fast-deep-equal";
import { assetsPath } from "./Common";
import "./Board.css";
import { GlobalContext } from "./GlobalContext";
import { Flock } from "./Bird";
import { Herd } from "./Sheep";
type BoardProps = {
animations: boolean;
};
type CornerData = {
index: number;
x: number;
y: number;
// Add other properties as needed
};
type RoadData = {
index: number;
x: number;
y: number;
// Add other properties as needed
};
type PipData = {
order: number;
x: number;
y: number;
// Add other properties as needed
};
type TileData = {
order: number;
x: number;
y: number;
// Add other properties as needed
};
type TurnData = {
// Define based on usage
};
type RulesData = {
// Define based on usage
};
type PlacementData = Record<string, unknown>;
type CornerProps = {
corner: CornerData;
};
type RoadProps = {
road: RoadData;
};
type PipProps = {
pip: PipData;
className?: string;
};
type TileProps = {
tile: TileData;
};
const rows = [3, 4, 5, 4, 3, 2]; /* The final row of 2 is to place roads and corners */
const hexRatio = 1.1547, // eslint-disable-line @typescript-eslint/no-loss-of-precision
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 showTooltip = () => {
const tooltip = document.querySelector(".Board .Tooltip") as HTMLElement;
if (tooltip) tooltip.style.display = "flex";
};
const clearTooltip = () => {
const tooltip = document.querySelector(".Board .Tooltip") as HTMLElement;
if (tooltip) tooltip.style.display = "none";
};
const Board: React.FC<BoardProps> = ({ animations }) => {
const { ws, sendJsonMessage } = useContext(GlobalContext);
const board = useRef();
const [transform, setTransform] = useState(1);
const [pipElements, setPipElements] = useState<React.ReactElement[]>([]);
const [borderElements, setBorderElements] = useState<React.ReactElement[]>([]);
const [tileElements, setTileElements] = useState<React.ReactElement[]>([]);
const [cornerElements, setCornerElements] = useState<React.ReactElement[]>([]);
const [roadElements, setRoadElements] = useState<React.ReactElement[]>([]);
const [signature, setSignature] = useState<string>("");
const [generated, setGenerated] = useState<string>("");
const [robber, setRobber] = useState<number>(-1);
const [robberName, setRobberName] = useState<string[]>([]);
const [pips, setPips] = useState<any>(undefined); // Keep as any for now, complex structure
const [pipOrder, setPipOrder] = useState<any>(undefined);
const [borders, setBorders] = useState<any>(undefined);
const [borderOrder, setBorderOrder] = useState<any>(undefined);
const [animationSeeds, setAnimationSeeds] = useState<any>(undefined);
const [tiles, setTiles] = useState<any>(undefined);
const [tileOrder, setTileOrder] = useState<number[]>([]);
const [placements, setPlacements] = useState<PlacementData | undefined>(undefined);
const [turn, setTurn] = useState<TurnData>({});
const [state, setState] = useState<string>("");
const [color, setColor] = useState<string>("");
const [rules, setRules] = useState<RulesData>({});
const [longestRoadLength, setLongestRoadLength] = useState<number>(0);
const fields = useMemo(
() => [
"signature",
"robber",
"robberName",
"pips",
"pipOrder",
"borders",
"borderOrder",
"tiles",
"tileOrder",
"placements",
"turn",
"state",
"color",
"longestRoadLength",
"rules",
"animationSeeds",
],
[]
);
const onWsMessage = (event) => {
if (ws && ws !== event.target) {
console.error(`Disconnect occur?`);
}
const data = JSON.parse(event.data);
switch (data.type) {
case "game-update":
console.log(`board - game update`, data.update);
if ("robber" in data.update && data.update.robber !== robber) {
setRobber(data.update.robber);
}
if ("robberName" in data.update && data.update.robberName !== robberName) {
setRobberName(data.update.robberName);
}
if ("state" in data.update && data.update.state !== state) {
setState(data.update.state);
}
if ("rules" in data.update && !equal(data.update.rules, rules)) {
setRules(data.update.rules);
}
if ("color" in data.update && data.update.color !== color) {
setColor(data.update.color);
}
if ("longestRoadLength" in data.update && data.update.longestRoadLength !== longestRoadLength) {
setLongestRoadLength(data.update.longestRoadLength);
}
if ("turn" in data.update) {
if (!equal(data.update.turn, turn)) {
console.log(`board - turn`, data.update.turn);
setTurn(data.update.turn);
}
}
if ("placements" in data.update && !equal(data.update.placements, placements)) {
console.log(`board - placements`, data.update.placements);
setPlacements(data.update.placements);
}
/* The following are only updated if there is a new game
* signature */
if ("pipOrder" in data.update && !equal(data.update.pipOrder, pipOrder)) {
console.log(`board - setting new pipOrder`);
setPipOrder(data.update.pipOrder);
}
if ("borderOrder" in data.update && !equal(data.update.borderOrder, borderOrder)) {
console.log(`board - setting new borderOrder`);
setBorderOrder(data.update.borderOrder);
}
if ("animationSeeds" in data.update && !equal(data.update.animationSeeds, animationSeeds)) {
console.log(`board - setting new animationSeeds`);
setAnimationSeeds(data.update.animationSeeds);
}
if ("tileOrder" in data.update && !equal(data.update.tileOrder, tileOrder)) {
console.log(`board - setting new tileOrder`);
setTileOrder(data.update.tileOrder);
}
if (data.update.signature !== signature) {
console.log(`board - setting new signature`);
setSignature(data.update.signature);
}
/* This is permanent static data from the server -- do not update
* once set */
if ("pips" in data.update && !pips) {
console.log(`board - setting new static pips`);
setPips(data.update.pips);
}
if ("tiles" in data.update && !tiles) {
console.log(`board - setting new static tiles`);
setTiles(data.update.tiles);
}
if ("borders" in data.update && !borders) {
console.log(`board - setting new static borders`);
setBorders(data.update.borders);
}
break;
default:
break;
}
};
const refWsMessage = useRef(onWsMessage);
useEffect(() => {
refWsMessage.current = onWsMessage;
});
useEffect(() => {
if (!ws) {
return;
}
console.log("board - bind");
const cbMessage = (e) => refWsMessage.current(e);
ws.addEventListener("message", cbMessage);
return () => {
console.log("board - unbind");
ws.removeEventListener("message", cbMessage);
};
}, [ws]);
useEffect(() => {
if (!sendJsonMessage) {
return;
}
sendJsonMessage({
type: "get",
fields,
});
}, [sendJsonMessage, fields]);
useEffect(() => {
const boardBox = board.current.querySelector(".BoardBox");
if (boardBox) {
console.log(`board - setting transform scale to ${transform}`);
boardBox.style.transform = `scale(${transform})`;
}
}, [transform]);
const onResize = () => {
if (!board.current) {
return;
}
/* Adjust the 'transform: scale' for the BoardBox
* so the board fills the Board
*
* The board is H tall, and H * hexRatio wide */
const width = board.current.offsetWidth,
height = board.current.offsetHeight;
let _transform;
if (height * hexRatio > width) {
_transform = width / (450 * hexRatio); // eslint-disable-line @typescript-eslint/no-loss-of-precision
} else {
_transform = height / 450; // eslint-disable-line @typescript-eslint/no-loss-of-precision
}
if (transform !== _transform) {
setTransform(_transform);
}
};
const refOnResize = useRef(onResize);
useEffect(() => {
refOnResize.current = onResize;
});
useEffect(() => {
const cbOnResize = (e) => refOnResize.current(e);
window.addEventListener("resize", cbOnResize);
return () => {
window.removeEventListener("resize", cbOnResize);
};
}, [refOnResize]);
onResize();
useEffect(() => {
if (!ws) {
return;
}
console.log(`Generating static corner data... should only occur once per reload or socket reconnect.`);
const onCornerClicked = (event, corner) => {
let type;
if (event.currentTarget.getAttribute("data-type") === "settlement") {
type = "place-city";
} else {
type = "place-settlement";
}
ws.send(
JSON.stringify({
type,
index: corner.index,
})
);
};
const Corner: React.FC<CornerProps> = ({ corner }) => {
return (
<div
className="Corner"
onMouseMove={(e) => {
if (e.shiftPressed) {
const tooltip = document.querySelector(".Board .Tooltip") as HTMLElement;
tooltip.innerHTML = `<pre>${corner}</pre>`;
showTooltip();
}
}}
onClick={(event) => {
onCornerClicked(event, corner);
}}
data-index={corner.index}
style={{
top: `${corner.top}px`,
left: `${corner.left}px`,
}}
>
<div className="Corner-Shape" />
</div>
);
};
const generateCorners = () => {
let row = 0,
rowCount = 0;
let y = -8 + 0.5 * tileHalfWidth - (rows.length - 1) * 0.5 * tileWidth,
x = -tileHalfHeight - (rows[row] - 1) * 0.5 * tileHeight;
let index = 0;
const corners = [];
let corner;
for (let i = 0; i < 21; i++) {
if (row > 2 && rowCount === 0) {
corner = {
index: index++,
top: y - 0.5 * tileHalfHeight,
left: x - tileHalfHeight,
};
corners.push(<Corner key={`corner-${index}}`} corner={corner} />);
}
corner = {
index: index++,
top: y,
left: x,
};
corners.push(<Corner key={`corner-${index}}`} corner={corner} />);
corner = {
index: index++,
top: y - 0.5 * tileHalfHeight,
left: x + tileHalfHeight,
};
corners.push(<Corner key={`corner-${index}}`} corner={corner} />);
if (++rowCount === rows[row]) {
corner = {
index: index++,
top: y,
left: x + 2 * tileHalfHeight,
};
corners.push(<Corner key={`corner-${index}}`} corner={corner} />);
if (row > 2) {
corner = {
index: index++,
top: y - 0.5 * tileHalfHeight,
left: x + 3 * tileHalfHeight,
};
corners.push(<Corner key={`corner-${index}}`} corner={corner} />);
}
row++;
rowCount = 0;
y += tileHeight - 10.5;
x = -tileHalfHeight - (rows[row] - 1) * 0.5 * tileHeight;
} else {
x += tileHeight;
}
}
return corners;
};
setCornerElements(generateCorners());
}, [ws, setCornerElements]);
useEffect(() => {
if (!ws) {
return;
}
console.log(`Generating static road data... should only occur once per reload or socket reconnect.`);
const Road: React.FC<RoadProps> = ({ road }) => {
const onRoadClicked = (road) => {
console.log(`Road clicked: ${road.index}`);
if (!ws) {
console.error(`board - onRoadClicked - ws is NULL`);
return;
}
ws.send(
JSON.stringify({
type: "place-road",
index: road.index,
})
);
};
return (
<div
className="Road"
onClick={() => {
onRoadClicked(road);
}}
data-index={road.index}
style={{
transform: `translate(-50%, -50%) rotate(${road.angle}deg)`,
top: `${road.top}px`,
left: `${road.left}px`,
}}
>
<div className="Road-Shape" />
</div>
);
};
const generateRoads = () => {
let row = 0,
rowCount = 0;
let y = -2.5 + tileHalfWidth - (rows.length - 1) * 0.5 * tileWidth,
x = -tileHalfHeight - (rows[row] - 1) * 0.5 * tileHeight;
let index = 0;
let road;
const corners = [];
for (let i = 0; i < 21; i++) {
const lastRow = row === rows.length - 1;
if (row > 2 && rowCount === 0) {
road = {
index: index++,
angle: -60,
top: y - 0.5 * tileHalfHeight,
left: x - tileHalfHeight,
};
corners.push(<Road key={`road-${index}}`} road={road} />);
}
road = {
index: index++,
angle: 240,
top: y,
left: x,
};
corners.push(<Road key={`road-${index}}`} road={road} />);
road = {
index: index++,
angle: -60,
top: y - 0.5 * tileHalfHeight,
left: x + tileHalfHeight,
};
corners.push(<Road key={`road-${index}}`} road={road} />);
if (!lastRow) {
road = {
index: index++,
angle: 0,
top: y,
left: x,
};
corners.push(<Road key={`road-${index}}`} road={road} />);
}
if (++rowCount === rows[row]) {
if (!lastRow) {
road = {
index: index++,
angle: 0,
top: y,
left: x + 2 * tileHalfHeight,
};
corners.push(<Road key={`road-${index}}`} road={road} />);
}
if (row > 2) {
road = {
index: index++,
angle: 60,
top: y - 0.5 * tileHalfHeight,
left: x + 3 * tileHalfHeight,
};
corners.push(<Road key={`road-${index}}`} road={road} />);
}
row++;
rowCount = 0;
y += tileHeight - 10.5;
x = -tileHalfHeight - (rows[row] - 1) * 0.5 * tileHeight;
} else {
x += tileHeight;
}
}
return corners;
};
setRoadElements(generateRoads());
}, [ws, setRoadElements]);
/* Generate Pip, Tile, and Border elements */
useEffect(() => {
if (!ws) {
return;
}
console.log(`board - Generate pip, border, and tile elements`);
const Pip: React.FC<PipProps> = ({ pip, className }) => {
const onPipClicked = (pip) => {
if (!ws) {
console.error(`board - sendPlacement - ws is NULL`);
return;
}
ws.send(
JSON.stringify({
type: "place-robber",
index: pip.index,
})
);
};
return (
<div
className={`Pip ${className}`}
onClick={() => {
onPipClicked(pip);
}}
data-roll={pip.roll}
data-index={pip.index}
style={{
top: `${pip.top}px`,
left: `${pip.left}px`,
backgroundImage: `url(${assetsPath}/gfx/pip-numbers.png)`,
backgroundPositionX: `${(100 * (pip.order % 6)) / 5}%`, // eslint-disable-line @typescript-eslint/no-loss-of-precision
backgroundPositionY: `${(100 * Math.floor(pip.order / 6)) / 5}%`,
}}
>
<div className="Pip-Shape" />
</div>
);
};
const generatePips = function (pipOrder) {
let row = 0,
rowCount = 0;
let y = tileHalfWidth - (rows.length - 1) * 0.5 * tileWidth,
x = -(rows[row] - 1) * 0.5 * tileHeight;
let index = 0;
let pip;
return pipOrder.map((order) => {
let volcano = false;
pip = {
roll: pips[order].roll,
index: index++,
top: y,
left: x,
order: order,
};
if ("volcano" in rules && rules[`volcano`].enabled && pip.roll === 7) {
pip.order = pips.findIndex((pip) => pip.roll === rules[`volcano`].number);
pip.roll = rules[`volcano`].number;
volcano = true;
}
const div = <Pip className={volcano ? "Volcano" : ""} pip={pip} key={`pip-${order}`} />;
if (++rowCount === rows[row]) {
row++;
rowCount = 0;
y += tileWidth;
x = -(rows[row] - 1) * 0.5 * tileHeight;
} else {
x += tileHeight;
}
return div;
});
};
const Tile: React.FC<TileProps> = ({ tile }) => {
const style: React.CSSProperties = {
top: `${tile.top}px`,
left: `${tile.left}px`,
width: `${tileImageWidth}px`,
height: `${tileImageHeight}px`,
backgroundImage: `url(${assetsPath}/gfx/tiles-${tile.type}.png)`,
backgroundPositionY: `-${tile.card * tileHeight}px`,
};
if (tile.type === "volcano") {
style.transform = `rotate(-90deg)`;
style.top = `${tile.top + 6}px`;
style.transformOrigin = "0% 50%";
}
return (
<div className="Tile" data-index={tile.index} style={{ ...style }}>
<div className="Tile-Shape" />
</div>
);
};
const generateTiles = function (tileOrder, animationSeeds) {
let row = 0,
rowCount = 0;
let y = tileHalfWidth - (rows.length - 1) * 0.5 * tileWidth,
x = -(rows[row] - 1) * 0.5 * tileHeight;
let index = 0;
return tileOrder.map((order) => {
const tile = Object.assign({}, tiles[order], { index: index++, left: x, top: y });
const volcanoActive = "volcano" in rules && rules[`volcano`].enabled;
if (
"tiles-start-facing-down" in rules &&
rules[`tiles-start-facing-down`].enabled &&
state !== "normal" &&
state !== "volcano" &&
state !== "winner" &&
(!volcanoActive || tile.type !== "desert")
) {
tile.type = "jungle";
tile.card = 0;
}
if (volcanoActive && tile.type === "desert") {
tile.type = "volcano";
tile.card = 0;
}
let div;
if (tile.type === "wheat") {
div = (
<div key={`tile-${order}`}>
{animations && (
<Flock
count={Math.floor(1 + animationSeeds[index] * 2)}
style={{
top: `${tile.top - tileImageHeight * 0.5}px`,
left: `${tile.left - tileImageWidth * 0.5}px`,
width: `${tileImageWidth}px`,
height: `${tileImageHeight}px`,
}}
/>
)}{" "}
<Tile tile={tile} />
</div>
);
} else if (tile.type === "sheep") {
div = (
<div key={`tile-${order}`}>
{animations && (
<Herd
count={Math.floor(1 + animationSeeds[index] * 4)}
style={{
top: `${tile.top - tileImageHeight * 0.5}px`,
left: `${tile.left - tileImageWidth * 0.5}px`,
width: `${tileImageWidth}px`,
height: `${tileImageHeight}px`,
}}
/>
)}
<Tile tile={tile} />
</div>
);
} else {
div = <Tile key={`tile-${order}`} tile={tile} />;
}
if (++rowCount === rows[row]) {
row++;
rowCount = 0;
y += tileWidth;
x = -(rows[row] - 1) * 0.5 * tileHeight;
} else {
x += tileHeight;
}
return div;
});
};
const calculateBorderSlot = (side, e) => {
const borderBox = document.querySelector(".Borders").getBoundingClientRect();
let angle =
((360 + Math.floor(90 + (Math.atan2(e.pageY - borderBox.top, e.pageX - borderBox.left) * 180) / Math.PI)) %
360) -
side * 60;
if (angle > 180) {
angle = angle - 360;
}
let slot = 0;
if (angle > -20 && angle < 5) {
slot = 1;
} else if (angle > 5) {
slot = 2;
}
return slot;
};
const mouseEnter = (border, side, e) => {
const slot = calculateBorderSlot(side, e);
if (!border[slot]) {
clearTooltip();
return;
}
const tooltip = document.querySelector(".Board .Tooltip") as HTMLElement;
tooltip.textContent =
border[slot] === "bank" ? "3 of one kind for 1 resource" : `2 ${border[slot]} for 1 resource`;
tooltip.style.top = `${e.pageY}px`;
tooltip.style.left = `${e.pageX + 16}px`;
showTooltip();
};
const mouseMove = (border, side, e) => {
const slot = calculateBorderSlot(side, e);
if (!border[slot]) {
clearTooltip();
return;
}
const tooltip = document.querySelector(".Board .Tooltip") as HTMLElement;
tooltip.textContent =
border[slot] === "bank" ? "3 of one kind for 1 resource" : `2 ${border[slot]} for 1 resource`;
tooltip.style.top = `${e.pageY}px`;
tooltip.style.left = `${e.pageX + 16}px`;
showTooltip();
};
const mouseLeave = () => {
clearTooltip();
};
const generateBorders = function (borderOrder) {
const sides = 6;
let side = -1;
return borderOrder.map((order) => {
const border = borders[order];
side++;
const x = +Math.sin(Math.PI - (side / sides) * 2 * Math.PI) * radius,
y = Math.cos(Math.PI - (side / sides) * 2 * Math.PI) * radius;
const prev = order === 0 ? 6 : order;
const file = `borders-${order + 1}.${prev}.png`;
const value = side;
return (
<div
key={`border-${order}`}
className="Border"
border={border}
onMouseEnter={(e) => {
mouseEnter(border, value, e);
}}
onMouseMove={(e) => {
mouseMove(border, value, e);
}}
onMouseLeave={mouseLeave}
style={{
width: `${borderImageWidth}px`,
height: `${borderImageHeight}px`,
top: `${y}px`,
left: `${x}px`,
transform: `rotate(${
side * (360 / sides)
}deg) translate(${borderOffsetX}px, ${borderOffsetY}px) scale(-1, -1)`,
backgroundImage: `url(${assetsPath}/gfx/${file} )`,
}}
/>
);
});
};
if (borders && borderOrder) {
console.log(`board - Generate board - borders`);
setBorderElements(generateBorders(borderOrder));
}
if (tiles && tileOrder && animationSeeds) {
console.log(`board - Generate board - tiles`);
setTileElements(generateTiles(tileOrder, animationSeeds));
}
/* Regenerate pips every time; it uses ws */
if (pips && pipOrder) {
console.log(`board - Generate board - pips`);
setPipElements(generatePips(pipOrder));
}
if (signature && signature !== generated) {
console.log(`board - Regnerating for ${signature}`);
setGenerated(signature);
}
}, [
signature,
generated,
pips,
pipOrder,
borders,
borderOrder,
tiles,
tileOrder,
animationSeeds,
ws,
state,
rules,
animations,
]);
/* Re-render turn info after every render */
useEffect(() => {
if (!turn) {
return;
}
let nodes = document.querySelectorAll(".Active");
for (let i = 0; i < nodes.length; i++) {
nodes[i].classList.remove("Active");
}
if (turn.roll) {
nodes = document.querySelectorAll(`.Pip[data-roll="${turn.roll}"]`);
for (let i = 0; i < nodes.length; i++) {
const index = nodes[i].getAttribute("data-index");
if (index !== null) {
const tile = document.querySelector(`.Tile[data-index="${index}"]`);
if (tile) {
tile.classList.add("Active");
}
}
nodes[i].classList.add("Active");
}
}
});
/* Re-render placements after every render */
useEffect(() => {
if (!placements) {
return;
}
/* Set color and type based on placement data from the server */
placements.corners.forEach((corner, index) => {
const el = document.querySelector(`.Corner[data-index="${index}"]`);
if (!el) {
return;
}
if (turn.volcano === index) {
el.classList.add("Lava");
} else {
el.classList.remove("Lava");
}
if (!corner.color) {
el.removeAttribute("data-color");
el.removeAttribute("data-type");
} else {
el.setAttribute("data-color", corner.color);
el.setAttribute("data-type", corner.type);
}
});
placements.roads.forEach((road, index) => {
const el = document.querySelector(`.Road[data-index="${index}"]`);
if (!el) {
return;
}
if (!road.color) {
el.removeAttribute("data-color");
} else {
if (road.longestRoad) {
if (road.longestRoad === longestRoadLength) {
el.classList.add("LongestRoad");
} else {
el.classList.remove("LongestRoad");
}
el.setAttribute("data-longest", road.longestRoad);
} else {
el.removeAttribute("data-longest");
}
el.setAttribute("data-color", road.color);
}
});
/* Clear all 'Option' targets */
let nodes = document.querySelectorAll(`.Option`);
for (let i = 0; i < nodes.length; i++) {
nodes[i].classList.remove("Option");
}
/* Add 'Option' based on turn.limits */
if (turn && turn.limits) {
if (turn.limits["roads"]) {
turn.limits["roads"].forEach((index) => {
const el = document.querySelector(`.Road[data-index="${index}"]`);
if (!el) {
return;
}
el.classList.add("Option");
el.setAttribute("data-color", turn.color);
});
}
if (turn.limits["corners"]) {
turn.limits["corners"].forEach((index) => {
const el = document.querySelector(`.Corner[data-index="${index}"]`);
if (!el) {
return;
}
el.classList.add("Option");
el.setAttribute("data-color", turn.color);
});
}
if (turn.limits["tiles"]) {
turn.limits["tiles"].forEach((index) => {
const el = document.querySelector(`.Tile[data-index="${index}"]`);
if (!el) {
return;
}
el.classList.add("Option");
});
}
if (turn.limits["pips"]) {
turn.limits["pips"].forEach((index) => {
const el = document.querySelector(`.Pip[data-index="${index}"]`);
if (!el) {
return;
}
el.classList.add("Option");
});
}
}
/* Clear the robber */
nodes = document.querySelectorAll(`.Pip.Robber`);
for (let i = 0; i < nodes.length; i++) {
nodes[i].classList.remove("Robber");
["Robert", "Roberta", "Velocirobber"].forEach((robberName) => nodes[i].classList.remove(robberName));
}
/* Place the robber */
if (robber !== undefined) {
const el = document.querySelector(`.Pip[data-index="${robber}"]`);
if (el) {
el.classList.add("Robber");
el.classList.add(robberName);
}
}
});
const canAction = (action) => {
return turn && Array.isArray(turn.actions) && turn.actions.indexOf(action) !== -1;
};
const canRoad =
canAction("place-road") && turn.color === color && (state === "initial-placement" || state === "normal");
const canCorner =
(canAction("place-settlement") || canAction("place-city")) &&
turn.color === color &&
(state === "initial-placement" || state === "normal");
const canPip =
canAction("place-robber") && turn.color === color && (state === "initial-placement" || state === "normal");
console.log(`board - tile elements`, tileElements);
return (
<div className="Board" ref={board}>
<div className="Tooltip">tooltip</div>
<div className="BoardBox">
<div className="Borders" disabled>
{borderElements}
</div>
<div className="Tiles" disabled>
{tileElements}
</div>
<div className="Pips" disabled={!canPip}>
{pipElements}
</div>
<div className="Corners" disabled={!canCorner}>
{cornerElements}
</div>
<div className="Roads" disabled={!canRoad}>
{roadElements}
</div>
</div>
</div>
);
};
export { Board };