From 8bbc6e1f622683a506a3e51f7648bec4a2eff972 Mon Sep 17 00:00:00 2001 From: MiguelMLorente Date: Wed, 4 Dec 2024 17:09:21 +0100 Subject: [PATCH] Implement special dice usage and layout --- web/src/pages/game/GamePage.scss | 1 + web/src/pages/game/GamePage.tsx | 67 ++++++++++++++++-- web/src/pages/game/components/BoardCell.tsx | 57 ++++++++-------- web/src/pages/game/components/DiceSet.scss | 14 +++- web/src/pages/game/components/DiceSet.tsx | 76 +++++++++------------ web/src/pages/game/components/Die.scss | 5 ++ web/src/pages/game/components/Die.tsx | 16 +---- web/src/pages/game/components/GameBoard.tsx | 27 +++----- web/src/pages/game/types/DieViewProps.ts | 9 +++ 9 files changed, 164 insertions(+), 108 deletions(-) create mode 100644 web/src/pages/game/types/DieViewProps.ts diff --git a/web/src/pages/game/GamePage.scss b/web/src/pages/game/GamePage.scss index 9847b6c..87b1eba 100644 --- a/web/src/pages/game/GamePage.scss +++ b/web/src/pages/game/GamePage.scss @@ -12,5 +12,6 @@ h1 { .right-panel { display: flex; flex-direction: column; + padding: 50px; } } diff --git a/web/src/pages/game/GamePage.tsx b/web/src/pages/game/GamePage.tsx index 4b06b3d..4e15ed6 100644 --- a/web/src/pages/game/GamePage.tsx +++ b/web/src/pages/game/GamePage.tsx @@ -3,6 +3,7 @@ import GameBoard from "./components/GameBoard"; import DiceSet from "./components/DiceSet"; import "./GamePage.scss"; import { PieceId } from "interface"; +import { DieViewProps } from "./types/DieViewProps"; const GamePage = () => { const getRandomPieceId = () => { @@ -12,22 +13,80 @@ const GamePage = () => { const [dice, setDice] = useState( [1, 2, 3, 4].map(() => { - return { + const dieViewProps: DieViewProps = { pieceId: getRandomPieceId(), isSelected: false, isDisabled: false, + isSpecial: false, + rotation: 0, + }; + return dieViewProps; + }), + ); + const [specialDice, setSpecialDice] = useState( + [ + PieceId.P03, + PieceId.P08, + PieceId.P13, + PieceId.P14, + PieceId.P15, + PieceId.P19, + ].map((pieceId) => { + return { + pieceId: pieceId, + isSelected: false, + isDisabled: false, + isSpecial: true, rotation: 0, }; }), ); + const [specialDieUsedInRound, setSpecialDieUsedInRound] = useState(false); + + const modifyDieState = ( + matcher: (die: DieViewProps) => boolean, + newStateComputer: (die: DieViewProps) => Partial, + ) => { + setDice( + dice.map((die) => { + if (!matcher(die)) return die; + return { + ...die, + ...newStateComputer(die), + }; + }), + ); + setSpecialDice( + specialDice.map((die) => { + if (!matcher(die)) return die; + return { + ...die, + ...newStateComputer(die), + }; + }), + ); + }; + + const fetchDie = (matcher: (die: DieViewProps) => boolean) => { + return dice.concat(specialDice).find((die) => matcher(die)); + }; return (

Game Page Title

- -
- + +
+
diff --git a/web/src/pages/game/components/BoardCell.tsx b/web/src/pages/game/components/BoardCell.tsx index ecfa25f..1c426a6 100644 --- a/web/src/pages/game/components/BoardCell.tsx +++ b/web/src/pages/game/components/BoardCell.tsx @@ -1,33 +1,32 @@ -import { Cell, CellType, directions, Exit, PieceId } from "interface"; +import { Cell, CellType, directions, Exit } from "interface"; import "./BoardCell.scss"; import { useState } from "react"; +import { DieViewProps } from "../types/DieViewProps"; export interface BoardCellProps { cell: Cell; - dice: { - pieceId: PieceId; - isSelected: boolean; - isDisabled: boolean; - rotation: number; - }[]; - setDice: React.Dispatch< - React.SetStateAction< - { - pieceId: PieceId; - isSelected: boolean; - isDisabled: boolean; - rotation: number; - }[] - > - >; refreshBoardRender: () => void; + modifyDieState: ( + matcher: (die: DieViewProps) => boolean, + newStateComputer: (die: DieViewProps) => Partial, + ) => void; + fetchDie: ( + matcher: (die: DieViewProps) => boolean, + ) => DieViewProps | undefined; + setSpecialDieUsedInRound: React.Dispatch>; } const BoardCell = (props: BoardCellProps) => { - const { cell, dice, setDice, refreshBoardRender } = props; + const { + cell, + refreshBoardRender, + modifyDieState, + fetchDie, + setSpecialDieUsedInRound, + } = props; const [pieceRotationAngle, setPieceRotationAngle] = useState(0); const handleBoardCellClick = () => { - const selectedDie = dice.find((die) => die.isSelected); + const selectedDie = fetchDie((die) => die.isSelected); if (!selectedDie) return; try { cell.placePiece( @@ -38,17 +37,17 @@ const BoardCell = (props: BoardCellProps) => { console.log(error); return; } - setDice( - dice.map((die) => { - return die !== selectedDie - ? die - : { - ...selectedDie, - isSelected: false, - isDisabled: true, - }; - }), + modifyDieState( + (die) => die === selectedDie, + () => { + return { + isSelected: false, + isDisabled: true, + }; + }, ); + if (selectedDie.isSpecial) setSpecialDieUsedInRound(true); + // Set rotation to the piece in the board, not the die setPieceRotationAngle(selectedDie.rotation); refreshBoardRender(); }; diff --git a/web/src/pages/game/components/DiceSet.scss b/web/src/pages/game/components/DiceSet.scss index 87776c6..da09ab7 100644 --- a/web/src/pages/game/components/DiceSet.scss +++ b/web/src/pages/game/components/DiceSet.scss @@ -1,9 +1,8 @@ .dice-set-actions { display: flex; flex-direction: row; - padding: 50px 50px 0; width: max-content; - margin: auto; + margin: auto auto 50px auto; img { border: 3px solid green; @@ -24,5 +23,14 @@ display: grid; grid-template-columns: repeat(2, auto); gap: 20px; - padding: 50px; + margin: 0 0 50px 0; +} + +.special-dice-set { + display: grid; + grid-template-columns: repeat(3, auto); + margin: 0 0 50px 0; + column-gap: 12%; + row-gap: 20px; + width: 100%; } \ No newline at end of file diff --git a/web/src/pages/game/components/DiceSet.tsx b/web/src/pages/game/components/DiceSet.tsx index 1779190..518e2a1 100644 --- a/web/src/pages/game/components/DiceSet.tsx +++ b/web/src/pages/game/components/DiceSet.tsx @@ -1,55 +1,42 @@ -import { PieceId } from "interface"; import "./DiceSet.scss"; import Die from "./Die"; import React from "react"; +import { DieViewProps } from "../types/DieViewProps"; export interface DiceSetProps { - dice: { - pieceId: PieceId; - isSelected: boolean; - isDisabled: boolean; - rotation: number; - }[]; - setDice: React.Dispatch< - React.SetStateAction< - { - pieceId: PieceId; - isSelected: boolean; - isDisabled: boolean; - rotation: number; - }[] - > - >; + dice: DieViewProps[]; + specialDice: DieViewProps[]; + modifyDieState: ( + matcher: (die: DieViewProps) => boolean, + newStateComputer: (die: DieViewProps) => Partial, + ) => void; + specialDieUsedInRound: boolean; } const DiceSet = (props: DiceSetProps) => { - const { dice, setDice } = props; - const handleDieClick = (die: { - pieceId: PieceId; - isSelected: boolean; - isDisabled: boolean; - rotation: number; - }) => { - if (die.isDisabled) return; - const newDiceState = dice.map((oldDie) => { - const isSelected = die === oldDie; - return { - ...oldDie, - isSelected: isSelected, - }; - }); - setDice(newDiceState); + const { dice, specialDice, modifyDieState, specialDieUsedInRound } = props; + const handleDieClick = (clickedDie: DieViewProps) => { + if (clickedDie.isDisabled) return; + const isSpecialDie = clickedDie.isSpecial; + if (isSpecialDie && specialDieUsedInRound) return; + modifyDieState( + () => true, + (die) => { + return { + isSelected: die === clickedDie, + }; + }, + ); }; const handleRotateButton = (rotation: number) => { - const newDiceState = dice.map((die) => { - if (!die.isSelected) return die; - const rotationAngle = (die.rotation + rotation + 360) % 360; - return { - ...die, - rotation: rotationAngle, - }; - }); - setDice(newDiceState); + modifyDieState( + (die) => die.isSelected, + (die) => { + return { + rotation: (die.rotation + rotation + 360) % 360, + }; + }, + ); }; return ( @@ -70,6 +57,11 @@ const DiceSet = (props: DiceSetProps) => { ))}
+
+ {specialDice.map((die) => ( + + ))} +
); }; diff --git a/web/src/pages/game/components/Die.scss b/web/src/pages/game/components/Die.scss index b3f4195..675582d 100644 --- a/web/src/pages/game/components/Die.scss +++ b/web/src/pages/game/components/Die.scss @@ -29,4 +29,9 @@ } transition: transform 0.5s cubic-bezier(.47,1.64,.41,.8); +} + +.special-dice-set .dice { + height: 80px; + width: 80px; } \ No newline at end of file diff --git a/web/src/pages/game/components/Die.tsx b/web/src/pages/game/components/Die.tsx index ba8474f..c039c39 100644 --- a/web/src/pages/game/components/Die.tsx +++ b/web/src/pages/game/components/Die.tsx @@ -1,19 +1,9 @@ -import { PieceId } from "interface"; import "./Die.scss"; +import { DieViewProps } from "../types/DieViewProps"; interface DieProps { - die: { - pieceId: PieceId; - isSelected: boolean; - isDisabled: boolean; - rotation: number; - }; - handleDieClick: (die: { - pieceId: PieceId; - isSelected: boolean; - isDisabled: boolean; - rotation: number; - }) => void; + die: DieViewProps; + handleDieClick: (die: DieViewProps) => void; } const Die = (props: DieProps) => { diff --git a/web/src/pages/game/components/GameBoard.tsx b/web/src/pages/game/components/GameBoard.tsx index 0997041..8ea33b9 100644 --- a/web/src/pages/game/components/GameBoard.tsx +++ b/web/src/pages/game/components/GameBoard.tsx @@ -1,25 +1,18 @@ -import { buildBoard, PieceId } from "interface"; +import { buildBoard } from "interface"; import "./GameBoard.scss"; import { useState } from "react"; import BoardCell from "./BoardCell"; +import { DieViewProps } from "../types/DieViewProps"; export interface GameBoardProps { - dice: { - pieceId: PieceId; - isSelected: boolean; - isDisabled: boolean; - rotation: number; - }[]; - setDice: React.Dispatch< - React.SetStateAction< - { - pieceId: PieceId; - isSelected: boolean; - isDisabled: boolean; - rotation: number; - }[] - > - >; + modifyDieState: ( + matcher: (die: DieViewProps) => boolean, + newStateComputer: (die: DieViewProps) => Partial, + ) => void; + fetchDie: ( + matcher: (die: DieViewProps) => boolean, + ) => DieViewProps | undefined; + setSpecialDieUsedInRound: React.Dispatch>; } const GameBoard = (props: GameBoardProps) => { diff --git a/web/src/pages/game/types/DieViewProps.ts b/web/src/pages/game/types/DieViewProps.ts new file mode 100644 index 0000000..05f049a --- /dev/null +++ b/web/src/pages/game/types/DieViewProps.ts @@ -0,0 +1,9 @@ +import { PieceId } from "interface"; + +export interface DieViewProps { + pieceId: PieceId; + isSelected: boolean; + isDisabled: boolean; + isSpecial: boolean; + rotation: number; +}