Improving House Rules UX
This commit is contained in:
parent
5e5f4ed9a9
commit
a3425b3178
@ -1,26 +1,4 @@
|
||||
.HouseRules {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 30rem;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background: rgba(0,0,0,0.5);
|
||||
z-index: 1000;
|
||||
max-height: 100vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.HouseRules > * {
|
||||
max-height: calc(100vh - 2em);
|
||||
overflow: auto;
|
||||
margin: 0.5em;
|
||||
width: 40em;
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.HouseRules .HouseSelector {
|
||||
display: flex;
|
||||
|
@ -1,14 +1,22 @@
|
||||
import React, { useState, useEffect, useContext, useRef, useMemo, useCallback } from "react";
|
||||
import React, {
|
||||
useState,
|
||||
useEffect,
|
||||
useContext,
|
||||
useRef,
|
||||
useMemo,
|
||||
useCallback,
|
||||
} from "react";
|
||||
import equal from "fast-deep-equal";
|
||||
|
||||
import Paper from "@mui/material/Paper";
|
||||
import Button from "@mui/material/Button";
|
||||
import Switch from "@mui/material/Switch";
|
||||
|
||||
import "./HouseRules.css";
|
||||
// import "./HouseRules.css";
|
||||
|
||||
import { GlobalContext } from "./GlobalContext";
|
||||
import { Placard } from "./Placard";
|
||||
import { Box } from "@mui/material";
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||
|
||||
@ -25,8 +33,12 @@ const Volcano: React.FC<VolcanoProps> = ({ ws, rules, field, disabled }) => {
|
||||
Math.random() > 0.5
|
||||
? Math.floor(8 + Math.random() * 5) /* Do not include 7 */
|
||||
: Math.floor(2 + Math.random() * 5); /* Do not include 7 */
|
||||
const [number, setNumber] = useState<number>(field in rules && "number" in rules[field] ? rules[field].number : init);
|
||||
const [gold, setGold] = useState<boolean>(field in rules && "gold" in rules[field] ? rules[field].gold : false);
|
||||
const [number, setNumber] = useState<number>(
|
||||
field in rules && "number" in rules[field] ? rules[field].number : init
|
||||
);
|
||||
const [gold, setGold] = useState<boolean>(
|
||||
field in rules && "gold" in rules[field] ? rules[field].gold : false
|
||||
);
|
||||
|
||||
console.log(`house-rules - ${field} - `, rules[field]);
|
||||
|
||||
@ -93,16 +105,21 @@ const Volcano: React.FC<VolcanoProps> = ({ ws, rules, field, disabled }) => {
|
||||
return (
|
||||
<div className="Volcano">
|
||||
<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!
|
||||
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>
|
||||
<div>
|
||||
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 settlement of its owner's color. If he has no settlements remaining, the
|
||||
city is destroyed instead.
|
||||
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
|
||||
settlement of its owner's color. If he has no settlements
|
||||
remaining, the city is destroyed instead.
|
||||
</div>
|
||||
<div>
|
||||
The presence of the Robber on the Volcano does not prevent the Volcano
|
||||
from erupting.
|
||||
</div>
|
||||
<div>The presence of the Robber on the Volcano does not prevent the Volcano from erupting.</div>
|
||||
<div>
|
||||
Roll {number} and the Volcano erupts!
|
||||
<button onClick={() => update(+1)}>up</button> /
|
||||
@ -110,20 +127,30 @@ const Volcano: React.FC<VolcanoProps> = ({ ws, rules, field, disabled }) => {
|
||||
</div>
|
||||
<div className="HouseSelector">
|
||||
<div>
|
||||
<b>Volcanoes have gold!</b>: Volcano can produce resources when its number is rolled.
|
||||
<b>Volcanoes have gold!</b>: Volcano can produce resources when its
|
||||
number is rolled.
|
||||
</div>
|
||||
<div>
|
||||
<Switch size={"small"} className="RuleSwitch" checked={gold} onChange={() => toggleGold()} {...{ disabled }} />
|
||||
<Switch
|
||||
size={"small"}
|
||||
className="RuleSwitch"
|
||||
checked={gold}
|
||||
onChange={() => toggleGold()}
|
||||
{...{ disabled }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
Volcanoes tend to be rich in valuable minerals such as gold or gems. Each settlement that is adjacent to the
|
||||
Volcano when it erupts may produce any one of the five resources it's owner desires.
|
||||
Volcanoes tend to be rich in valuable minerals such as gold or gems.
|
||||
Each settlement that is adjacent to the Volcano when it erupts may
|
||||
produce any one of the five resources it's owner desires.
|
||||
</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.
|
||||
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>
|
||||
</div>
|
||||
);
|
||||
@ -186,8 +213,7 @@ const HouseRules: React.FC<HouseRulesProps> = ({ houseRulesActive }) => {
|
||||
const { ws, name } = useContext(GlobalContext);
|
||||
const [rules, setRules] = useState<any>({});
|
||||
const [state, setState] = useState<any>({});
|
||||
const [gameState, setGameState] = useState<string>('');
|
||||
const [ruleElements, setRuleElements] = useState<React.ReactElement[]>([]);
|
||||
const [gameState, setGameState] = useState<string>("");
|
||||
|
||||
const fields = useMemo(() => ["state", "rules"], []);
|
||||
const onWsMessage = (event: MessageEvent) => {
|
||||
@ -259,8 +285,8 @@ const HouseRules: React.FC<HouseRulesProps> = ({ houseRulesActive }) => {
|
||||
[rules, ws]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const ruleList = [
|
||||
const ruleList = useMemo(
|
||||
() => [
|
||||
{
|
||||
key: "volcano",
|
||||
label: "Volcanoes are a lava fun!",
|
||||
@ -268,7 +294,12 @@ const HouseRules: React.FC<HouseRulesProps> = ({ houseRulesActive }) => {
|
||||
category: "board",
|
||||
defaultChecked: false,
|
||||
element: (
|
||||
<Volcano ws={ws} rules={rules} field={"volcano"} disabled={gameState !== 'lobby'} />
|
||||
<Volcano
|
||||
ws={ws}
|
||||
rules={rules}
|
||||
field={"volcano"}
|
||||
disabled={gameState !== "lobby"}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
@ -277,20 +308,29 @@ const HouseRules: React.FC<HouseRulesProps> = ({ houseRulesActive }) => {
|
||||
description: "Customize how many Victory Points are required to win.",
|
||||
category: "rules",
|
||||
defaultChecked: false,
|
||||
element: <VictoryPoints ws={ws} rules={rules} field={"victory-points"} />,
|
||||
element: (
|
||||
<VictoryPoints ws={ws} rules={rules} field={"victory-points"} />
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "tiles-start-facing-down",
|
||||
label: "Tiles start facing down",
|
||||
description: "Resource tiles start upside-down while placing starting settlements.",
|
||||
description:
|
||||
"Resource tiles start upside-down while placing starting settlements.",
|
||||
category: "board",
|
||||
defaultChecked: false,
|
||||
element: <div>Once all players have placed their initial settlements and roads, the tiles are flipped and you discover what the resources are.</div>,
|
||||
element: (
|
||||
<div>
|
||||
Once all players have placed their initial settlements and roads,
|
||||
the tiles are flipped and you discover what the resources are.
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "most-developed",
|
||||
label: "You are so developed",
|
||||
description: "The player with the most development cards (more than 4) receives 2VP.",
|
||||
description:
|
||||
"The player with the most development cards (more than 4) receives 2VP.",
|
||||
category: "expansion",
|
||||
defaultChecked: false,
|
||||
element: <Placard type="most-developed" />,
|
||||
@ -298,7 +338,8 @@ const HouseRules: React.FC<HouseRulesProps> = ({ houseRulesActive }) => {
|
||||
{
|
||||
key: "port-of-call",
|
||||
label: "Another round of port",
|
||||
description: "The player with the most harbor ports (more than 2) receives 2VP.",
|
||||
description:
|
||||
"The player with the most harbor ports (more than 2) receives 2VP.",
|
||||
category: "expansion",
|
||||
defaultChecked: false,
|
||||
element: <Placard type="port-of-call" />,
|
||||
@ -306,7 +347,8 @@ const HouseRules: React.FC<HouseRulesProps> = ({ houseRulesActive }) => {
|
||||
{
|
||||
key: "slowest-turn",
|
||||
label: "Why you play so slowf",
|
||||
description: "The player with the longest turn idle time (longer than 2 minutes) so far loses 2VP.",
|
||||
description:
|
||||
"The player with the longest turn idle time (longer than 2 minutes) so far loses 2VP.",
|
||||
category: "expansion",
|
||||
defaultChecked: false,
|
||||
element: <Placard type="longest-turn" />,
|
||||
@ -317,7 +359,12 @@ const HouseRules: React.FC<HouseRulesProps> = ({ houseRulesActive }) => {
|
||||
description: "Roll again if you roll two of the same number.",
|
||||
category: "rolling",
|
||||
defaultChecked: false,
|
||||
element: <div>If you roll doubles, players get those resources and then you must roll again.</div>,
|
||||
element: (
|
||||
<div>
|
||||
If you roll doubles, players get those resources and then you must
|
||||
roll again.
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "twelve-and-two-are-synonyms",
|
||||
@ -325,20 +372,44 @@ const HouseRules: React.FC<HouseRulesProps> = ({ houseRulesActive }) => {
|
||||
description: "If twelve is rolled, two scores as well. And vice-versa.",
|
||||
category: "rolling",
|
||||
defaultChecked: false,
|
||||
element: <div>If you roll a twelve or two, resources are triggered for both.</div>,
|
||||
element: (
|
||||
<div>
|
||||
If you roll a twelve or two, resources are triggered for both.
|
||||
</div>
|
||||
),
|
||||
},
|
||||
{
|
||||
key: "robin-hood-robber",
|
||||
label: "Robin Hood robber",
|
||||
description: "Robbers can't steal from players with two or less victory points.",
|
||||
description:
|
||||
"Robbers can't steal from players with two or less victory points.",
|
||||
category: "rules",
|
||||
defaultChecked: false,
|
||||
element: <></>,
|
||||
},
|
||||
];
|
||||
],
|
||||
[rules, setRules, state, ws, setRule, name, gameState]
|
||||
);
|
||||
|
||||
setRuleElements(
|
||||
ruleList.map((item) => {
|
||||
if (!houseRulesActive) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Paper
|
||||
className="HouseRules"
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center",
|
||||
alignItems: "center",
|
||||
maxHeight: "100dvh",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<Box className="Title">House Rules</Box>
|
||||
<Box sx={{ display: "flex", flexDirection: "column" }}>
|
||||
{ruleList.map((item) => {
|
||||
const defaultChecked = item.defaultChecked;
|
||||
if (!(item.key in rules)) {
|
||||
rules[item.key] = {
|
||||
@ -352,7 +423,9 @@ const HouseRules: React.FC<HouseRulesProps> = ({ houseRulesActive }) => {
|
||||
|
||||
return (
|
||||
<div key={item.key} className="HouseSelector">
|
||||
<div><b>{item.label}</b>: {item.description}</div>
|
||||
<div>
|
||||
<b>{item.label}</b>: {item.description}
|
||||
</div>
|
||||
<div>
|
||||
<Switch
|
||||
size={"small"}
|
||||
@ -360,28 +433,16 @@ const HouseRules: React.FC<HouseRulesProps> = ({ houseRulesActive }) => {
|
||||
checked={checked}
|
||||
id={item.key}
|
||||
onChange={(e) => setRule(e, item.key)}
|
||||
disabled={gameState !== 'lobby' || !name}
|
||||
disabled={gameState !== "lobby" || !name}
|
||||
/>
|
||||
</div>
|
||||
{checked && item.element}
|
||||
</div>
|
||||
);
|
||||
})
|
||||
);
|
||||
}, [rules, setRules, setRuleElements, state, ws, setRule, name, gameState]);
|
||||
|
||||
if (!houseRulesActive) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="HouseRules">
|
||||
<Paper>
|
||||
<div className="Title">House Rules</div>
|
||||
<div style={{ display: "flex", flexDirection: "column" }}>{ruleElements}</div>
|
||||
})}
|
||||
</Box>
|
||||
<Button onClick={dismissClicked}>Close</Button>
|
||||
</Paper>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user