Added modal to select character to steal from, Added button component
This commit is contained in:
parent
cf195e8e43
commit
68fe8192aa
26
pac-man-board-game/ClientApp/src/components/Button.tsx
Normal file
26
pac-man-board-game/ClientApp/src/components/Button.tsx
Normal file
@ -0,0 +1,26 @@
|
||||
import React from "react";
|
||||
|
||||
export const Button: Component<ButtonProps> = (
|
||||
{
|
||||
className,
|
||||
onClick,
|
||||
style,
|
||||
title,
|
||||
id,
|
||||
disabled = false,
|
||||
children,
|
||||
type = "button",
|
||||
}) => {
|
||||
return (
|
||||
<button
|
||||
id={id}
|
||||
className={`button-default ${className}`}
|
||||
style={style}
|
||||
disabled={disabled}
|
||||
type={type}
|
||||
title={title}
|
||||
onClick={onClick}>
|
||||
{children}
|
||||
</button>
|
||||
)
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import React from "react";
|
||||
import {useAtom} from "jotai";
|
||||
import {selectedDiceAtom} from "../utils/state";
|
||||
import {useAtom, useAtomValue} from "jotai";
|
||||
import {selectedDiceAtom, thisPlayerAtom} from "../utils/state";
|
||||
import {Button} from "./Button";
|
||||
|
||||
interface AllDiceProps extends ComponentProps {
|
||||
values?: number[],
|
||||
@ -41,6 +42,8 @@ export const Dice: Component<DiceProps> = (
|
||||
onClick,
|
||||
}) => {
|
||||
|
||||
const thisPlayer = useAtomValue(thisPlayerAtom);
|
||||
|
||||
function handleClick() {
|
||||
if (onClick && value) {
|
||||
onClick(value);
|
||||
@ -48,8 +51,10 @@ export const Dice: Component<DiceProps> = (
|
||||
}
|
||||
|
||||
return (
|
||||
<button className={`text-2xl bg-gray-400 px-4 m-1 ${className}`} onClick={handleClick}>
|
||||
<Button className={`text-2xl bg-gray-400 px-4 m-1 ${className}`}
|
||||
disabled={!thisPlayer?.isTurn()}
|
||||
onClick={handleClick}>
|
||||
{value?.toString()}
|
||||
</button>
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
@ -1,17 +1,20 @@
|
||||
import React, {useEffect, useState} from "react";
|
||||
import React, {Fragment, useEffect, useState} from "react";
|
||||
import {Character} from "../game/character";
|
||||
import findPossiblePositions from "../game/possibleMovesAlgorithm";
|
||||
import {GameTile} from "./gameTile";
|
||||
import {TileType} from "../game/tileType";
|
||||
import {useAtomValue} from "jotai";
|
||||
import {allCharactersAtom, currentPlayerAtom, selectedDiceAtom} from "../utils/state";
|
||||
import {atom, PrimitiveAtom, useAtom, useAtomValue, useSetAtom} from "jotai";
|
||||
import {allCharactersAtom, currentPlayerAtom, playersAtom, selectedDiceAtom} from "../utils/state";
|
||||
import Pellet from "../game/pellet";
|
||||
import {Dialog, Transition} from "@headlessui/react";
|
||||
|
||||
interface BoardProps extends ComponentProps {
|
||||
onMove?: Action<Position[]>,
|
||||
map: GameMap
|
||||
}
|
||||
|
||||
const modalOpenAtom: PrimitiveAtom<boolean> = atom(false);
|
||||
|
||||
const Board: Component<BoardProps> = (
|
||||
{
|
||||
className,
|
||||
@ -25,6 +28,7 @@ const Board: Component<BoardProps> = (
|
||||
const [selectedCharacter, setSelectedCharacter] = useState<Character>();
|
||||
const [possiblePositions, setPossiblePositions] = useState<Path[]>([]); // TODO reset when other client moves a character
|
||||
const [hoveredPosition, setHoveredPosition] = useState<Path>();
|
||||
const setModalOpen = useSetAtom(modalOpenAtom);
|
||||
|
||||
function handleSelectCharacter(character: Character): void {
|
||||
if (character.isPacMan() && currentPlayer?.PacMan.Colour !== character.Colour) {
|
||||
@ -61,12 +65,8 @@ const Board: Component<BoardProps> = (
|
||||
}
|
||||
}
|
||||
|
||||
// TODO steal from other player
|
||||
function stealFromPlayer(): void {
|
||||
// TODO select player to steal from
|
||||
// TODO modal to select player
|
||||
// const victim
|
||||
// currentPlayer?.stealFrom(victim)
|
||||
setModalOpen(true);
|
||||
}
|
||||
|
||||
function pickUpPellets(destination: Path): Position[] {
|
||||
@ -103,6 +103,7 @@ const Board: Component<BoardProps> = (
|
||||
|
||||
return (
|
||||
<div className={`w-fit ${className}`}>
|
||||
<SelectPlayerModal/>
|
||||
{
|
||||
map.map((row, rowIndex) =>
|
||||
<div key={rowIndex} className={"flex"}>
|
||||
@ -128,3 +129,88 @@ const Board: Component<BoardProps> = (
|
||||
};
|
||||
|
||||
export default Board;
|
||||
|
||||
const SelectPlayerModal: Component = () => {
|
||||
const [isOpen, setIsOpen] = useAtom(modalOpenAtom);
|
||||
const currentPlayer = useAtomValue(currentPlayerAtom);
|
||||
const allPlayers = useAtomValue(playersAtom).filter(p => p !== currentPlayer);
|
||||
|
||||
if (currentPlayer === undefined) return null;
|
||||
|
||||
function close(): void {
|
||||
setIsOpen(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Transition appear show={isOpen} as={Fragment}>
|
||||
<Dialog as="div" className="relative z-10" onClose={close}>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-black bg-opacity-25"/>
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 overflow-y-auto">
|
||||
<div className="flex min-h-full items-center justify-center p-4 text-center">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 scale-95"
|
||||
enterTo="opacity-100 scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 scale-100"
|
||||
leaveTo="opacity-0 scale-95"
|
||||
>
|
||||
<Dialog.Panel
|
||||
className="w-full max-w-md transform overflow-hidden rounded-2xl bg-white p-6 text-left align-middle shadow-xl transition-all">
|
||||
<Dialog.Title as="h3" className="text-lg font-medium leading-6 text-gray-900">
|
||||
Steal from player
|
||||
</Dialog.Title>
|
||||
<div className="mt-2">
|
||||
<Dialog.Description className="text-sm text-gray-500">
|
||||
Select a player to steal up to 2 pellets from.
|
||||
</Dialog.Description>
|
||||
</div>
|
||||
|
||||
{
|
||||
allPlayers.map(player =>
|
||||
<div key={player.Name} className={"border-b pb-1"}>
|
||||
<span className={"mx-2"}>{player.Name} has {player.Box.count} pellets</span>
|
||||
<button className={"text-blue-500 enabled:cursor-pointer disabled:text-gray-500"}
|
||||
style={{background: "none"}}
|
||||
disabled={player.Box.count === 0}
|
||||
onClick={() => {
|
||||
currentPlayer?.stealFrom(player);
|
||||
close();
|
||||
}}>
|
||||
Steal
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
<div className="mt-4">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex justify-center rounded-md border border-transparent bg-blue-100 px-4 py-2 text-sm font-medium text-blue-900 hover:bg-blue-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 focus-visible:ring-offset-2"
|
||||
onClick={close}
|
||||
>
|
||||
Don't steal from anyone
|
||||
</button>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import React, {MouseEventHandler} from "react";
|
||||
import {State} from "../game/player";
|
||||
import {currentPlayerAtom, rollDiceButtonAtom, thisPlayerAtom} from "../utils/state";
|
||||
import {useAtomValue} from "jotai";
|
||||
import {Button} from "./Button";
|
||||
|
||||
interface GameButtonProps extends ComponentProps {
|
||||
onReadyClick?: MouseEventHandler,
|
||||
@ -19,12 +20,12 @@ const GameButton: Component<GameButtonProps> = (
|
||||
const activeRollDiceButton = useAtomValue(rollDiceButtonAtom);
|
||||
|
||||
if (currentPlayer === undefined || currentPlayer.State === State.waitingForPlayers) {
|
||||
return <button onClick={onReadyClick}>Ready</button>;
|
||||
return <Button onClick={onReadyClick}>Ready</Button>;
|
||||
}
|
||||
if (!thisPlayer?.isTurn()) { // TODO also show when waiting for other players
|
||||
return <button disabled>Please wait</button>;
|
||||
return <Button disabled>Please wait</Button>;
|
||||
}
|
||||
return <button onClick={onRollDiceClick} disabled={!activeRollDiceButton}>Roll dice</button>;
|
||||
return <Button onClick={onRollDiceClick} disabled={!activeRollDiceButton}>Roll dice</Button>;
|
||||
};
|
||||
|
||||
export default GameButton;
|
||||
|
@ -13,13 +13,13 @@ import GameButton from "./gameButton";
|
||||
|
||||
const wsService = new WebSocketService(import.meta.env.VITE_API);
|
||||
|
||||
// TODO ghost should be able to take pacmen
|
||||
// TODO bug, when taking player on last dice, the currentPlayer changes and the wrong character get to steal
|
||||
|
||||
// TODO move stats above dice
|
||||
// TODO don't start game until at least 2 players have joined
|
||||
// TODO join game lobby
|
||||
// TODO sign up player page
|
||||
// TODO sign in page
|
||||
// TODO steal from other players
|
||||
// TODO show box with collected pellets
|
||||
// TODO layout
|
||||
|
||||
|
@ -2,7 +2,7 @@ import {Character, CharacterType} from "./character";
|
||||
import Box from "./box";
|
||||
import {Colour} from "./colour";
|
||||
import {getDefaultStore} from "jotai";
|
||||
import {currentPlayerNameAtom} from "../utils/state";
|
||||
import {currentPlayerNameAtom, playersAtom} from "../utils/state";
|
||||
import Pellet from "./pellet";
|
||||
|
||||
export enum State {
|
||||
@ -44,6 +44,8 @@ export default class Player {
|
||||
if (pellet)
|
||||
this.Box.addPellet(pellet);
|
||||
}
|
||||
const store = getDefaultStore();
|
||||
store.set(playersAtom, store.get(playersAtom).map(player => player));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -23,10 +23,7 @@ br {
|
||||
@apply my-2;
|
||||
}
|
||||
|
||||
button, button[type=submit], input[type=button], input[type=submit] {
|
||||
.button-default, button[type=submit], input[type=submit] {
|
||||
@apply bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded;
|
||||
}
|
||||
|
||||
button[disabled] {
|
||||
@apply bg-gray-500;
|
||||
@apply disabled:bg-gray-500;
|
||||
}
|
||||
|
@ -10,7 +10,13 @@ interface ComponentProps {
|
||||
}
|
||||
|
||||
interface ChildProps extends ComponentProps {
|
||||
children?: React.JSX.Element,
|
||||
children?: React.JSX.Element | string,
|
||||
}
|
||||
|
||||
interface ButtonProps extends ChildProps {
|
||||
onClick?: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void,
|
||||
disabled?: boolean,
|
||||
type?: "button" | "submit" | "reset",
|
||||
}
|
||||
|
||||
interface InputProps extends ComponentProps {
|
||||
|
Loading…
x
Reference in New Issue
Block a user