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 {
display: flex;
flex-direction: column;
padding: 50px;
}
}

View File

@ -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<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 (
<React.Fragment>
<h1>Game Page Title</h1>
<div className="game-panel">
<GameBoard dice={dice} setDice={setDice} />
<div className="rigth-panel">
<DiceSet dice={dice} setDice={setDice} />
<GameBoard
modifyDieState={modifyDieState}
fetchDie={fetchDie}
setSpecialDieUsedInRound={setSpecialDieUsedInRound}
/>
<div className="right-panel">
<DiceSet
dice={dice}
specialDice={specialDice}
modifyDieState={modifyDieState}
specialDieUsedInRound={specialDieUsedInRound}
/>
</div>
</div>
</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 { 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<DieViewProps>,
) => void;
fetchDie: (
matcher: (die: DieViewProps) => boolean,
) => DieViewProps | undefined;
setSpecialDieUsedInRound: React.Dispatch<React.SetStateAction<boolean>>;
}
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();
};

View File

@ -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%;
}

View File

@ -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<DieViewProps>,
) => 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) => {
<Die die={die} handleDieClick={handleDieClick} />
))}
</div>
<div className="special-dice-set">
{specialDice.map((die) => (
<Die die={die} handleDieClick={handleDieClick} />
))}
</div>
</React.Fragment>
);
};

View File

@ -30,3 +30,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 { 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) => {

View File

@ -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<DieViewProps>,
) => void;
fetchDie: (
matcher: (die: DieViewProps) => boolean,
) => DieViewProps | undefined;
setSpecialDieUsedInRound: React.Dispatch<React.SetStateAction<boolean>>;
}
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;
}