Implement special dice usage and layout

landing-page-layout
MiguelMLorente 2024-12-04 17:09:21 +01:00
parent f04f153a0e
commit 8bbfe472c6
9 changed files with 164 additions and 108 deletions

View File

@ -12,5 +12,6 @@ h1 {
.right-panel { .right-panel {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding: 50px;
} }
} }

View File

@ -3,6 +3,7 @@ import GameBoard from "./components/GameBoard";
import DiceSet from "./components/DiceSet"; import DiceSet from "./components/DiceSet";
import "./GamePage.scss"; import "./GamePage.scss";
import { PieceId } from "interface"; import { PieceId } from "interface";
import { DieViewProps } from "./types/DieViewProps";
const GamePage = () => { const GamePage = () => {
const getRandomPieceId = () => { const getRandomPieceId = () => {
@ -12,22 +13,80 @@ const GamePage = () => {
const [dice, setDice] = useState( const [dice, setDice] = useState(
[1, 2, 3, 4].map(() => { [1, 2, 3, 4].map(() => {
return { const dieViewProps: DieViewProps = {
pieceId: getRandomPieceId(), pieceId: getRandomPieceId(),
isSelected: false, isSelected: false,
isDisabled: 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, rotation: 0,
}; };
}), }),
); );
const [specialDieUsedInRound, setSpecialDieUsedInRound] = useState(false);
const modifyDieState = (
matcher: (die: DieViewProps) => boolean,
newStateComputer: (die: DieViewProps) => Partial<DieViewProps>,
) => {
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 ( return (
<React.Fragment> <React.Fragment>
<h1>Game Page Title</h1> <h1>Game Page Title</h1>
<div className="game-panel"> <div className="game-panel">
<GameBoard dice={dice} setDice={setDice} /> <GameBoard
<div className="rigth-panel"> modifyDieState={modifyDieState}
<DiceSet dice={dice} setDice={setDice} /> fetchDie={fetchDie}
setSpecialDieUsedInRound={setSpecialDieUsedInRound}
/>
<div className="right-panel">
<DiceSet
dice={dice}
specialDice={specialDice}
modifyDieState={modifyDieState}
specialDieUsedInRound={specialDieUsedInRound}
/>
</div> </div>
</div> </div>
</React.Fragment> </React.Fragment>

View File

@ -1,33 +1,32 @@
import { Cell, CellType, directions, Exit, PieceId } from "interface"; import { Cell, CellType, directions, Exit } from "interface";
import "./BoardCell.scss"; import "./BoardCell.scss";
import { useState } from "react"; import { useState } from "react";
import { DieViewProps } from "../types/DieViewProps";
export interface BoardCellProps { export interface BoardCellProps {
cell: Cell; 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; refreshBoardRender: () => void;
modifyDieState: (
matcher: (die: DieViewProps) => boolean,
newStateComputer: (die: DieViewProps) => Partial<DieViewProps>,
) => void;
fetchDie: (
matcher: (die: DieViewProps) => boolean,
) => DieViewProps | undefined;
setSpecialDieUsedInRound: React.Dispatch<React.SetStateAction<boolean>>;
} }
const BoardCell = (props: BoardCellProps) => { const BoardCell = (props: BoardCellProps) => {
const { cell, dice, setDice, refreshBoardRender } = props; const {
cell,
refreshBoardRender,
modifyDieState,
fetchDie,
setSpecialDieUsedInRound,
} = props;
const [pieceRotationAngle, setPieceRotationAngle] = useState(0); const [pieceRotationAngle, setPieceRotationAngle] = useState(0);
const handleBoardCellClick = () => { const handleBoardCellClick = () => {
const selectedDie = dice.find((die) => die.isSelected); const selectedDie = fetchDie((die) => die.isSelected);
if (!selectedDie) return; if (!selectedDie) return;
try { try {
cell.placePiece( cell.placePiece(
@ -38,17 +37,17 @@ const BoardCell = (props: BoardCellProps) => {
console.log(error); console.log(error);
return; return;
} }
setDice( modifyDieState(
dice.map((die) => { (die) => die === selectedDie,
return die !== selectedDie () => {
? die return {
: {
...selectedDie,
isSelected: false, isSelected: false,
isDisabled: true, isDisabled: true,
}; };
}), },
); );
if (selectedDie.isSpecial) setSpecialDieUsedInRound(true);
// Set rotation to the piece in the board, not the die
setPieceRotationAngle(selectedDie.rotation); setPieceRotationAngle(selectedDie.rotation);
refreshBoardRender(); refreshBoardRender();
}; };

View File

@ -1,9 +1,8 @@
.dice-set-actions { .dice-set-actions {
display: flex; display: flex;
flex-direction: row; flex-direction: row;
padding: 50px 50px 0;
width: max-content; width: max-content;
margin: auto; margin: auto auto 50px auto;
img { img {
border: 3px solid green; border: 3px solid green;
@ -24,5 +23,14 @@
display: grid; display: grid;
grid-template-columns: repeat(2, auto); grid-template-columns: repeat(2, auto);
gap: 20px; 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%;
} }

View File

@ -1,55 +1,42 @@
import { PieceId } from "interface";
import "./DiceSet.scss"; import "./DiceSet.scss";
import Die from "./Die"; import Die from "./Die";
import React from "react"; import React from "react";
import { DieViewProps } from "../types/DieViewProps";
export interface DiceSetProps { export interface DiceSetProps {
dice: { dice: DieViewProps[];
pieceId: PieceId; specialDice: DieViewProps[];
isSelected: boolean; modifyDieState: (
isDisabled: boolean; matcher: (die: DieViewProps) => boolean,
rotation: number; newStateComputer: (die: DieViewProps) => Partial<DieViewProps>,
}[]; ) => void;
setDice: React.Dispatch< specialDieUsedInRound: boolean;
React.SetStateAction<
{
pieceId: PieceId;
isSelected: boolean;
isDisabled: boolean;
rotation: number;
}[]
>
>;
} }
const DiceSet = (props: DiceSetProps) => { const DiceSet = (props: DiceSetProps) => {
const { dice, setDice } = props; const { dice, specialDice, modifyDieState, specialDieUsedInRound } = props;
const handleDieClick = (die: { const handleDieClick = (clickedDie: DieViewProps) => {
pieceId: PieceId; if (clickedDie.isDisabled) return;
isSelected: boolean; const isSpecialDie = clickedDie.isSpecial;
isDisabled: boolean; if (isSpecialDie && specialDieUsedInRound) return;
rotation: number; modifyDieState(
}) => { () => true,
if (die.isDisabled) return; (die) => {
const newDiceState = dice.map((oldDie) => {
const isSelected = die === oldDie;
return { return {
...oldDie, isSelected: die === clickedDie,
isSelected: isSelected,
}; };
}); },
setDice(newDiceState); );
}; };
const handleRotateButton = (rotation: number) => { const handleRotateButton = (rotation: number) => {
const newDiceState = dice.map((die) => { modifyDieState(
if (!die.isSelected) return die; (die) => die.isSelected,
const rotationAngle = (die.rotation + rotation + 360) % 360; (die) => {
return { return {
...die, rotation: (die.rotation + rotation + 360) % 360,
rotation: rotationAngle,
}; };
}); },
setDice(newDiceState); );
}; };
return ( return (
@ -70,6 +57,11 @@ const DiceSet = (props: DiceSetProps) => {
<Die die={die} handleDieClick={handleDieClick} /> <Die die={die} handleDieClick={handleDieClick} />
))} ))}
</div> </div>
<div className="special-dice-set">
{specialDice.map((die) => (
<Die die={die} handleDieClick={handleDieClick} />
))}
</div>
</React.Fragment> </React.Fragment>
); );
}; };

View File

@ -30,3 +30,8 @@
transition: transform 0.5s cubic-bezier(.47,1.64,.41,.8); transition: transform 0.5s cubic-bezier(.47,1.64,.41,.8);
} }
.special-dice-set .dice {
height: 80px;
width: 80px;
}

View File

@ -1,19 +1,9 @@
import { PieceId } from "interface";
import "./Die.scss"; import "./Die.scss";
import { DieViewProps } from "../types/DieViewProps";
interface DieProps { interface DieProps {
die: { die: DieViewProps;
pieceId: PieceId; handleDieClick: (die: DieViewProps) => void;
isSelected: boolean;
isDisabled: boolean;
rotation: number;
};
handleDieClick: (die: {
pieceId: PieceId;
isSelected: boolean;
isDisabled: boolean;
rotation: number;
}) => void;
} }
const Die = (props: DieProps) => { const Die = (props: DieProps) => {

View File

@ -1,25 +1,18 @@
import { buildBoard, PieceId } from "interface"; import { buildBoard } from "interface";
import "./GameBoard.scss"; import "./GameBoard.scss";
import { useState } from "react"; import { useState } from "react";
import BoardCell from "./BoardCell"; import BoardCell from "./BoardCell";
import { DieViewProps } from "../types/DieViewProps";
export interface GameBoardProps { export interface GameBoardProps {
dice: { modifyDieState: (
pieceId: PieceId; matcher: (die: DieViewProps) => boolean,
isSelected: boolean; newStateComputer: (die: DieViewProps) => Partial<DieViewProps>,
isDisabled: boolean; ) => void;
rotation: number; fetchDie: (
}[]; matcher: (die: DieViewProps) => boolean,
setDice: React.Dispatch< ) => DieViewProps | undefined;
React.SetStateAction< setSpecialDieUsedInRound: React.Dispatch<React.SetStateAction<boolean>>;
{
pieceId: PieceId;
isSelected: boolean;
isDisabled: boolean;
rotation: number;
}[]
>
>;
} }
const GameBoard = (props: GameBoardProps) => { const GameBoard = (props: GameBoardProps) => {

View File

@ -0,0 +1,9 @@
import { PieceId } from "interface";
export interface DieViewProps {
pieceId: PieceId;
isSelected: boolean;
isDisabled: boolean;
isSpecial: boolean;
rotation: number;
}