House Rules almost done
13
.github/copilot-instructions.md
vendored
@ -10,8 +10,11 @@ Guidelines for contributors and automated assistants
|
|||||||
- Use the provided Docker and docker-compose workflows for development and
|
- Use the provided Docker and docker-compose workflows for development and
|
||||||
building. The repository's `Dockerfile` and `docker-compose.yml` are the
|
building. The repository's `Dockerfile` and `docker-compose.yml` are the
|
||||||
canonical way to install dependencies and run the app.
|
canonical way to install dependencies and run the app.
|
||||||
- If you need to run a command locally for quick checks, prefer run commands in
|
- For development mode (hot-reload), set `PRODUCTION=0` and run `./launch.sh`.
|
||||||
the project's container environment. Example (copy-paste):
|
- For production mode (static build), set `PRODUCTION=1` and run `./launch.sh`.
|
||||||
|
- For manual building of the production image, run `docker-compose build`.
|
||||||
|
- If you need to run a command for quick checks, use the project's container
|
||||||
|
environment. Example (copy-paste):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose run --rm peddlers-of-ketran bash
|
docker compose run --rm peddlers-of-ketran bash
|
||||||
@ -20,12 +23,6 @@ Guidelines for contributors and automated assistants
|
|||||||
npm run build
|
npm run build
|
||||||
```
|
```
|
||||||
|
|
||||||
- For lightweight commands, you can run one-off containers:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker run --rm -v "$PWD":/app -w /app node:20 bash -lc 'cd client && npm ci && npm run type-check'
|
|
||||||
```
|
|
||||||
|
|
||||||
- Do not suggest or perform `npm install` or `npm ci` on the host machine in
|
- Do not suggest or perform `npm install` or `npm ci` on the host machine in
|
||||||
repository files, PRs, or automated edits. If a change requires dependency
|
repository files, PRs, or automated edits. If a change requires dependency
|
||||||
updates, update package.json and lockfiles via a CI job or in-container run,
|
updates, update package.json and lockfiles via a CI job or in-container run,
|
||||||
|
@ -23,8 +23,22 @@ import TableRow from "@mui/material/TableRow";
|
|||||||
|
|
||||||
// import "./HouseRules.css";
|
// import "./HouseRules.css";
|
||||||
|
|
||||||
|
import boardImg from "./assets/category-board.png";
|
||||||
|
import expansionImg from "./assets/category-expansion.png";
|
||||||
|
import rollingImg from "./assets/category-rolling.png";
|
||||||
|
import rulesImg from "./assets/category-rules.png";
|
||||||
|
import volcanoTile from "./assets/single-volcano.png";
|
||||||
|
|
||||||
import { GlobalContext } from "./GlobalContext";
|
import { GlobalContext } from "./GlobalContext";
|
||||||
import { Placard } from "./Placard";
|
import { Placard } from "./Placard";
|
||||||
|
import Box from "@mui/material/Box/Box";
|
||||||
|
|
||||||
|
const categoryImages: { [key: string]: string } = {
|
||||||
|
board: boardImg,
|
||||||
|
expansion: expansionImg,
|
||||||
|
rolling: rollingImg,
|
||||||
|
rules: rulesImg,
|
||||||
|
};
|
||||||
|
|
||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
@ -111,13 +125,36 @@ const Volcano: React.FC<VolcanoProps> = ({ ws, rules, field, disabled }) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="Volcano">
|
<Box
|
||||||
<div>
|
className="Volcano"
|
||||||
The Volcano replaces the Desert. When the Volcano erupts, roll a die to
|
sx={{
|
||||||
determine the direction the lava will flow. One of the six intersections
|
display: "flex",
|
||||||
on the Volcano tile will be affected. If there is a settlement on the
|
flexDirection: "column",
|
||||||
selected intersection, it is destroyed!
|
alignItems: "center",
|
||||||
</div>
|
gap: 2,
|
||||||
|
marginTop: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src={volcanoTile}
|
||||||
|
alt={"Volcano"}
|
||||||
|
style={{ width: "100px", height: "100px" }}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
The Volcano replaces the Desert. When the Volcano erupts, roll a die
|
||||||
|
to determine the direction the lava will flow. One of the six
|
||||||
|
intersections on the Volcano tile will be affected. If there is a
|
||||||
|
settlement on the selected intersection, it is destroyed!
|
||||||
|
</div>
|
||||||
|
</Box>
|
||||||
<div>
|
<div>
|
||||||
Remove it from the board (its owner may rebuild it later). If a city is
|
Remove it from the board (its owner may rebuild it later). If a city is
|
||||||
located there, it is reduced to a settlement! Replace the city with a
|
located there, it is reduced to a settlement! Replace the city with a
|
||||||
@ -129,38 +166,53 @@ const Volcano: React.FC<VolcanoProps> = ({ ws, rules, field, disabled }) => {
|
|||||||
from erupting.
|
from erupting.
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
Roll {number} and the Volcano erupts!
|
Roll <b>{number}</b> and the Volcano erupts!
|
||||||
<button onClick={() => update(+1)}>up</button> /
|
<Button onClick={() => update(+1)}>up</Button> /
|
||||||
<button onClick={() => update(-1)}> down</button>
|
<Button onClick={() => update(-1)}> down</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className="HouseSelector">
|
<Paper sx={{ flexGrow: 1, width: "100%" }}>
|
||||||
<div>
|
<Table>
|
||||||
<b>Volcanoes have gold!</b>: Volcano can produce resources when its
|
<TableRow>
|
||||||
number is rolled.
|
<TableCell>
|
||||||
</div>
|
<b>Volcanoes have gold!</b>
|
||||||
<div>
|
<br />
|
||||||
<Switch
|
Volcano can produce resources when its number is rolled.
|
||||||
size={"small"}
|
</TableCell>
|
||||||
className="RuleSwitch"
|
<TableCell>
|
||||||
checked={gold}
|
<Switch
|
||||||
onChange={() => toggleGold()}
|
size={"small"}
|
||||||
{...{ disabled }}
|
className="RuleSwitch"
|
||||||
/>
|
checked={gold}
|
||||||
</div>
|
onChange={() => toggleGold()}
|
||||||
</div>
|
{...{ disabled }}
|
||||||
<div>
|
/>
|
||||||
Volcanoes tend to be rich in valuable minerals such as gold or gems.
|
</TableCell>
|
||||||
Each settlement that is adjacent to the Volcano when it erupts may
|
</TableRow>
|
||||||
produce any one of the five resources it's owner desires.
|
{gold && (
|
||||||
</div>
|
<TableRow>
|
||||||
<div>
|
<TableCell colSpan={3}>
|
||||||
Each city adjacent to the Volcano may produce any two resources. This
|
<Box sx={{ display: "flex", flexDirection: "column", gap: 2 }}>
|
||||||
resource production is taken before the results of the volcano eruption
|
<div>
|
||||||
are resolved. Note that while the Robber can not prevent the Volcano
|
Volcanoes tend to be rich in valuable minerals such as gold
|
||||||
from erupting, he does prevent any player from producing resources from
|
or gems. Each settlement that is adjacent to the Volcano
|
||||||
the Volcano hex if he has been placed there.
|
when it erupts may produce any one of the five resources
|
||||||
</div>
|
it's owner desires.
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
Each city adjacent to the Volcano may produce any two
|
||||||
|
resources. This resource production is taken before the
|
||||||
|
results of the volcano eruption are resolved. Note that
|
||||||
|
while the Robber can not prevent the Volcano from erupting,
|
||||||
|
he does prevent any player from producing resources from the
|
||||||
|
Volcano hex if he has been placed there.
|
||||||
|
</div>
|
||||||
|
</Box>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</Table>
|
||||||
|
</Paper>
|
||||||
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -205,9 +257,9 @@ const VictoryPoints: React.FC<VictoryPointsProps> = ({ ws, rules, field }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="VictoryPoints">
|
<div className="VictoryPoints">
|
||||||
{points} points.
|
<b>{points}</b> points.
|
||||||
<button onClick={() => update(+1)}>up</button> /
|
<Button onClick={() => update(+1)}>up</Button> /
|
||||||
<button onClick={() => update(-1)}> down</button>
|
<Button onClick={() => update(-1)}> down</Button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@ -336,7 +388,7 @@ const HouseRules: React.FC<HouseRulesProps> = ({ houseRulesActive }) => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "most-developed",
|
key: "most-developed",
|
||||||
label: "You are so developed",
|
label: "You are so developed!",
|
||||||
description:
|
description:
|
||||||
"The player with the most development cards (more than 4) receives 2VP.",
|
"The player with the most development cards (more than 4) receives 2VP.",
|
||||||
category: "expansion",
|
category: "expansion",
|
||||||
@ -345,7 +397,7 @@ const HouseRules: React.FC<HouseRulesProps> = ({ houseRulesActive }) => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "port-of-call",
|
key: "port-of-call",
|
||||||
label: "Another round of port",
|
label: "Another round of port?",
|
||||||
description:
|
description:
|
||||||
"The player with the most harbor ports (more than 2) receives 2VP.",
|
"The player with the most harbor ports (more than 2) receives 2VP.",
|
||||||
category: "expansion",
|
category: "expansion",
|
||||||
@ -354,7 +406,7 @@ const HouseRules: React.FC<HouseRulesProps> = ({ houseRulesActive }) => {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "slowest-turn",
|
key: "slowest-turn",
|
||||||
label: "Why you play so slowf",
|
label: "Why you play so slow?",
|
||||||
description:
|
description:
|
||||||
"The player with the longest turn idle time (longer than 2 minutes) so far loses 2VP.",
|
"The player with the longest turn idle time (longer than 2 minutes) so far loses 2VP.",
|
||||||
category: "expansion",
|
category: "expansion",
|
||||||
@ -395,7 +447,7 @@ const HouseRules: React.FC<HouseRulesProps> = ({ houseRulesActive }) => {
|
|||||||
defaultChecked: false,
|
defaultChecked: false,
|
||||||
element: <></>,
|
element: <></>,
|
||||||
},
|
},
|
||||||
],
|
].sort((a, b) => a.category.localeCompare(b.category)),
|
||||||
[rules, setRules, state, ws, setRule, name, gameState]
|
[rules, setRules, state, ws, setRule, name, gameState]
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -404,12 +456,21 @@ const HouseRules: React.FC<HouseRulesProps> = ({ houseRulesActive }) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper sx={{ p: 1, maxWidth: 600, margin: "1rem auto", flexDirection: "column", display: "flex" }} className="HouseRules" elevation={3}>
|
<Paper
|
||||||
|
sx={{
|
||||||
|
p: 1,
|
||||||
|
maxWidth: 600,
|
||||||
|
margin: "1rem auto",
|
||||||
|
flexDirection: "column",
|
||||||
|
display: "flex",
|
||||||
|
}}
|
||||||
|
className="HouseRules"
|
||||||
|
elevation={3}
|
||||||
|
>
|
||||||
<DialogTitle>House Rules</DialogTitle>
|
<DialogTitle>House Rules</DialogTitle>
|
||||||
<DialogContent>
|
<DialogContent>
|
||||||
<TableContainer>
|
<TableContainer>
|
||||||
<Table>
|
<Table sx={{ tableLayout: "fixed" }}>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
{ruleList.map((item) => {
|
{ruleList.map((item) => {
|
||||||
const defaultChecked = item.defaultChecked;
|
const defaultChecked = item.defaultChecked;
|
||||||
@ -426,11 +487,20 @@ const HouseRules: React.FC<HouseRulesProps> = ({ houseRulesActive }) => {
|
|||||||
return (
|
return (
|
||||||
<React.Fragment key={item.key}>
|
<React.Fragment key={item.key}>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell>
|
<TableCell sx={{ width: "50px" }}>
|
||||||
<b>{item.label}</b>
|
{/* Fixed width for image */}
|
||||||
|
<img
|
||||||
|
src={categoryImages[item.category]}
|
||||||
|
alt={item.category}
|
||||||
|
style={{ width: "50px", height: "68px" }}
|
||||||
|
/>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
<TableCell>{item.description}</TableCell>
|
<TableCell sx={{ width: "auto" }}>
|
||||||
<TableCell>
|
<b>{item.label}</b>
|
||||||
|
<br />
|
||||||
|
{item.description}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell sx={{ width: "100px" }}>
|
||||||
<Switch
|
<Switch
|
||||||
size={"small"}
|
size={"small"}
|
||||||
className="RuleSwitch"
|
className="RuleSwitch"
|
||||||
|
BIN
client/src/assets/category-board.png
Executable file
After Width: | Height: | Size: 631 KiB |
BIN
client/src/assets/category-expansion.png
Executable file
After Width: | Height: | Size: 646 KiB |
BIN
client/src/assets/category-rolling.png
Executable file
After Width: | Height: | Size: 668 KiB |
BIN
client/src/assets/category-rules.png
Executable file
After Width: | Height: | Size: 638 KiB |
BIN
client/src/assets/single-volcano.png
Executable file
After Width: | Height: | Size: 716 KiB |
BIN
client/src/assets/tiles-brick.png
Normal file
After Width: | Height: | Size: 539 KiB |
BIN
client/src/assets/tiles-desert.png
Normal file
After Width: | Height: | Size: 144 KiB |
BIN
client/src/assets/tiles-jungle.png
Executable file
After Width: | Height: | Size: 156 KiB |
BIN
client/src/assets/tiles-sheep.png
Normal file
After Width: | Height: | Size: 607 KiB |
BIN
client/src/assets/tiles-stone.png
Normal file
After Width: | Height: | Size: 576 KiB |
BIN
client/src/assets/tiles-volcano.png
Executable file
After Width: | Height: | Size: 144 KiB |
BIN
client/src/assets/tiles-wheat.png
Normal file
After Width: | Height: | Size: 721 KiB |
BIN
client/src/assets/tiles-wood.png
Normal file
After Width: | Height: | Size: 723 KiB |
2
client/src/global.d.ts
vendored
@ -7,6 +7,8 @@ declare module 'Trade' {
|
|||||||
|
|
||||||
declare module '*.svg';
|
declare module '*.svg';
|
||||||
|
|
||||||
|
declare module '*.png';
|
||||||
|
|
||||||
declare module "./Trade" {
|
declare module "./Trade" {
|
||||||
const Trade: unknown;
|
const Trade: unknown;
|
||||||
export default Trade;
|
export default Trade;
|
||||||
|