1
0
James Ketrenos 0df5160428 Implementing initial-placement
Signed-off-by: James Ketrenos <james_eikona@ketrenos.com>
2022-02-05 16:43:18 -08:00

466 lines
13 KiB
JavaScript

import React, { useState } from "react";
import "./Board.css";
const base = process.env.PUBLIC_URL;
const assetsPath = `${base}/assets`;
const Board = ({ game }) => {
const rows = [3, 4, 5, 4, 3, 2]; /* The final row of 2 is to place roads and corners */
const [signature, setSignature] = useState("");
const [pips, setPips] = useState(<></>);
const [borders, setBorders] = useState(<></>);
const [tiles, setTiles] = useState(<></>);
const [corners, setCorners] = useState(<></>);
const [roads, setRoads] = useState(<></>);
const [transform, setTransform] = useState(1);
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 onClick = (event) => {
console.log(`Tile clicked: ${tile.index}`);
let nodes = document.querySelectorAll('.Tile.Selected');
for (let i = 0; i < nodes.length; i++) {
const el = nodes[i];
if (el !== event.target) {
el.classList.remove('Selected');
}
}
nodes = document.querySelectorAll('.Corner.Selected');
for (let i = 0; i < nodes.length; i++) {
const el = nodes[i];
if (el !== event.target) {
el.classList.remove('Selected');
}
}
nodes = document.querySelectorAll('.Road.Selected');
for (let i = 0; i < nodes.length; i++) {
const el = nodes[i];
if (el !== event.target) {
el.classList.remove('Selected');
}
}
game.layout.tiles[tile.index].corners.forEach(index => {
const el = document.querySelector(`.Corner[data-index="${index}"]`);
if (!el) {
console.log(`Unable to find corner[${index}]`);
} else {
el.classList.add('Selected');
}
});
game.layout.tiles[tile.index].roads.forEach(index => {
const el = document.querySelector(`.Road[data-index="${index}"]`);
if (!el) {
console.log(`Unable to find corner[${index}]`);
} else {
el.classList.add('Selected');
}
});
event.target.setAttribute("data-color", game.color);
event.target.classList.toggle('Selected');
};
return <div className="Tile"
onClick={onClick}
data-index={tile.index}
style={{
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`
}}
></div>;
};
const Road = ({road}) => {
const onClick = (event) => {
console.log(`Road clicked: ${road.index}`);
let nodes = document.querySelectorAll('.Road.Selected');
for (let i = 0; i < nodes.length; i++) {
const el = nodes[i];
if (el !== event.target) {
el.classList.remove('Selected');
}
}
nodes = document.querySelectorAll('.Corner.Selected');
for (let i = 0; i < nodes.length; i++) {
const el = nodes[i];
if (el !== event.target) {
el.classList.remove('Selected');
}
}
game.layout.roads[road.index].corners.forEach(index => {
const el = document.querySelector(`.Corner[data-index="${index}"]`);
if (!el) {
console.log(`Unable to find corner[${index}]`);
} else {
el.classList.add('Selected');
}
});
event.target.setAttribute("data-color", game.color);
event.target.classList.toggle('Selected');
};
return <div className="Road"
onClick={onClick}
data-index={road.index}
style={{
transform: `translate(-50%, -50%) rotate(${road.angle}deg)`,
top: `${road.top}px`,
left: `${road.left}px`
}}
></div>;
};
const Corner = ({corner}) => {
const onClick = (event) => {
console.log(`Corner ${corner.index}:`, game.layout.corners[corner.index]);
let nodes = document.querySelectorAll('.Corner.Selected');
for (let i = 0; i < nodes.length; i++) {
const el = nodes[i];
if (el !== event.target) {
el.classList.remove('Selected');
}
}
nodes = document.querySelectorAll('.Road.Selected');
for (let i = 0; i < nodes.length; i++) {
const el = nodes[i];
if (el !== event.target) {
el.classList.remove('Selected');
}
}
game.layout.corners[corner.index].roads.forEach(index => {
const el = document.querySelector(`.Road[data-index="${index}"]`);
if (!el) {
console.log(`Unable to find road[${index}]`);
} else {
el.classList.add('Selected');
}
});
event.target.classList.toggle('Selected');
event.target.setAttribute("data-color", game.color);
};
return <div className="Corner"
onClick={onClick}
data-index={corner.index}
style={{
top: `${corner.top}px`,
left: `${corner.left}px`
}}
></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;
}
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;
}
const generatePips = () => {
let row = 0, rowCount = 0;
let y = tileHalfWidth - (rows.length - 1) * 0.5 * tileWidth,
x = -(rows[row] - 1) * 0.5 * tileHeight;
return game.pipOrder.map(order => {
const pip = game.pips[order];
const div = <div
pip={pip}
key={`pip-${order}`}
className="Pip"
style={{
top: `${y}px`,
left: `${x}px`,
backgroundImage: `url(${assetsPath}/gfx/pip-numbers.png)`,
backgroundPositionX: `${ 100. * (order % 6) / 5.}%`,
backgroundPositionY: `${ 100 * Math.floor(order / 6) / 5. }%`
}}
/>;
if (++rowCount === rows[row]) {
row++;
rowCount = 0;
y += tileWidth;
x = - (rows[row] - 1) * 0.5 * tileHeight;
} else {
x += tileHeight;
}
return div;
});
};
const generateTiles = () => {
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 game.tileOrder.map(order => {
const tile = Object.assign({},
game.tiles[order],
{ index: index++, left: x, top: y});
let div = <Tile
key={`${tile.type}-${tile.card}`}
tile={tile}
/>;
if (++rowCount === rows[row]) {
row++;
rowCount = 0;
y += tileWidth;
x = - (rows[row] - 1) * 0.5 * tileHeight;
} else {
x += tileHeight;
}
return div;
});
};
const generateBorders = () => {
const sides = 6;
let side = -1;
return game.borderOrder.map(order => {
const border = game.borders[order];
side++;
let x = + Math.sin(Math.PI - side / sides * 2. * Math.PI) * radius,
y = Math.cos(Math.PI - side / sides * 2. * Math.PI) * radius;
let prev = (order === 0) ? 6 : order;
const file = `borders-${order+1}.${prev}.png`;
return <div
key={`border-${order}`}
className="Border"
border={border}
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 the game is loaded, and the signature is different,
* regenerate everything */
if (game && game.signature !== signature) {
console.log(`Generate for ${game.signature}`);
setPips(generatePips);
setBorders(generateBorders);
setTiles(generateTiles);
setCorners(generateCorners);
setRoads(generateRoads);
setSignature(game.signature);
} else {
if (!game) {
console.log(`No game in Board`);
}
}
/* Adjust the 'transform: scale' for the BoardBox
* so the board fills the Board
*
* The board is H tall, and H * hexRatio wide */
const board = document.querySelector('.Board');
if (board) {
const width = board.offsetWidth,
height = board.offsetHeight;
let _transform;
if (height * hexRatio > width) {
_transform = width / (450. * hexRatio);
} else {
_transform = height / (450.);
}
if (_transform !== transform) {
const boardBox = board.querySelector('.BoardBox');
if (boardBox) {
console.log(`Setting transofrm scale to ${_transform}`);
boardBox.style.transform = `scale(${_transform})`;
}
setTransform(_transform);
}
}
const canAction = (action) => {
console.log(game.turn);
return (game && game.turn && Array.isArray(game.turn.actions) && game.turn.actions.indexOf(action) !== -1);
};
return (
<div className="Board">
<div className="BoardBox">
{ borders }
{ tiles }
{ pips }
{ game && <>
<div className="Corners" disabled={!canAction('place-settlement') || game.turn.color !== game.color || (game.state !== 'initial-placement' && game.state !== 'normal')}>
{ corners }
</div>
<div className="Roads" disabled={!canAction('place-road') || game.turn.color !== game.color || (game.state !== 'initial-placement' && game.state !== 'normal')}>
{ roads }
</div>
</> }
</div>
</div>
);
};
export default Board;