Implemented Jotai state management
This commit is contained in:
parent
35295b9705
commit
5be200cc56
17
pac-man-board-game/ClientApp/package-lock.json
generated
17
pac-man-board-game/ClientApp/package-lock.json
generated
@ -10,6 +10,7 @@
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^1.7.14",
|
||||
"@heroicons/react": "^2.0.18",
|
||||
"jotai": "^2.2.2",
|
||||
"oidc-client": "^1.11.5",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
@ -2235,6 +2236,22 @@
|
||||
"jiti": "bin/jiti.js"
|
||||
}
|
||||
},
|
||||
"node_modules/jotai": {
|
||||
"version": "2.2.2",
|
||||
"resolved": "https://registry.npmjs.org/jotai/-/jotai-2.2.2.tgz",
|
||||
"integrity": "sha512-Cn8hnBg1sc5ppFwEgVGTfMR5WSM0hbAasd/bdAwIwTaum0j3OUPqBSC4tyk3jtB95vicML+RRWgKFOn6gtfN0A==",
|
||||
"engines": {
|
||||
"node": ">=12.20.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=17.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/js-string-escape": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/js-string-escape/-/js-string-escape-1.0.1.tgz",
|
||||
|
@ -5,6 +5,7 @@
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^1.7.14",
|
||||
"@heroicons/react": "^2.0.18",
|
||||
"jotai": "^2.2.2",
|
||||
"oidc-client": "^1.11.5",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
|
@ -1,30 +1,24 @@
|
||||
import React, {useEffect, useState} from "react";
|
||||
import React, {useEffect} from "react";
|
||||
import {AllDice} from "./dice";
|
||||
import {GameAction} from "../utils/actions";
|
||||
import {doAction, GameAction} from "../utils/actions";
|
||||
import GameBoard from "./gameBoard";
|
||||
import {Character, Ghost, PacMan} from "../game/character";
|
||||
import WebSocketService from "../websockets/WebSocketService";
|
||||
import {testMap} from "../game/map";
|
||||
import {TileType} from "../game/tileType";
|
||||
import Player, {State} from "../game/player";
|
||||
import {Colour} from "../game/colour";
|
||||
import PlayerStats from "../components/playerStats";
|
||||
import {useAtom} from "jotai";
|
||||
import {charactersAtom, currentPlayerAtom, diceAtom, playersAtom, selectedDiceAtom} from "../utils/state";
|
||||
|
||||
const wsService = new WebSocketService(import.meta.env.VITE_API);
|
||||
|
||||
const ghosts = [
|
||||
new Ghost({Colour: Colour.Purple}),
|
||||
new Ghost({Colour: Colour.Purple}),
|
||||
];
|
||||
|
||||
export const GameComponent: Component<{ player: Player }> = ({player}) => {
|
||||
// TODO find spawn points
|
||||
const [characters, setCharacters] = useState<Character[]>();
|
||||
const [players, setPlayers] = useState<Player[]>([player]);
|
||||
const [characters] = useAtom(charactersAtom);
|
||||
const [players] = useAtom(playersAtom);
|
||||
|
||||
const [dice, setDice] = useState<number[]>();
|
||||
const [selectedDice, setSelectedDice] = useState<SelectedDice>();
|
||||
const [currentPlayer, setCurrentPlayer] = useState<Player>();
|
||||
const [dice] = useAtom(diceAtom);
|
||||
const [selectedDice, setSelectedDice] = useAtom(selectedDiceAtom);
|
||||
const [currentPlayer] = useAtom(currentPlayerAtom);
|
||||
|
||||
function handleDiceClick(selected: SelectedDice): void {
|
||||
setSelectedDice(selected);
|
||||
@ -45,57 +39,6 @@ export const GameComponent: Component<{ player: Player }> = ({player}) => {
|
||||
rollDice();
|
||||
}
|
||||
|
||||
function doAction(message: MessageEvent<string>): void { // TODO move to actions.ts
|
||||
const parsed: ActionMessage = JSON.parse(message.data);
|
||||
|
||||
switch (parsed.Action) {
|
||||
case GameAction.rollDice:
|
||||
setDice(parsed.Data as number[]);
|
||||
break;
|
||||
case GameAction.moveCharacter:
|
||||
setDice(parsed.Data?.dice as number[]);
|
||||
updateCharacters(parsed);
|
||||
removeEatenPellets(parsed);
|
||||
break;
|
||||
case GameAction.playerInfo:
|
||||
const playerProps = parsed.Data as PlayerProps[];
|
||||
console.log(playerProps);
|
||||
setPlayers(playerProps.map(p => new Player(p)));
|
||||
const pacMen = playerProps.filter(p => p.PacMan).map(p => new PacMan(p.PacMan!));
|
||||
console.log(pacMen);
|
||||
// TODO find spawn points
|
||||
setCharacters([...pacMen, ...ghosts]);
|
||||
break;
|
||||
case GameAction.ready:
|
||||
const isReady = parsed.Data.AllReady as boolean;
|
||||
if (isReady) {
|
||||
setCurrentPlayer(new Player(parsed.Data.Starter as PlayerProps));
|
||||
}
|
||||
setPlayers((parsed.Data.Players as PlayerProps[]).map(p => new Player(p)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function removeEatenPellets(parsed: ActionMessage): void {
|
||||
const pellets = parsed.Data?.eatenPellets as Position[];
|
||||
|
||||
for (const pellet of pellets) {
|
||||
testMap[pellet.y][pellet.x] = TileType.empty;
|
||||
}
|
||||
}
|
||||
|
||||
function updateCharacters(parsed: ActionMessage): void {
|
||||
const updatedCharacters = parsed.Data?.characters as CharacterProps[] | undefined;
|
||||
|
||||
if (updatedCharacters) {
|
||||
const newList: Character[] = [];
|
||||
for (const character of updatedCharacters) {
|
||||
newList.push(new Character(character));
|
||||
}
|
||||
setCharacters(newList);
|
||||
}
|
||||
}
|
||||
|
||||
function onCharacterMove(eatenPellets: Position[]): void {
|
||||
if (dice && selectedDice) {
|
||||
dice.splice(selectedDice.index, 1);
|
||||
@ -122,8 +65,7 @@ export const GameComponent: Component<{ player: Player }> = ({player}) => {
|
||||
wsService.open();
|
||||
|
||||
void sendPlayer();
|
||||
// TODO send action to backend when all players are ready
|
||||
// The backend should then send the first player as current player
|
||||
|
||||
return () => wsService.close();
|
||||
}, []);
|
||||
|
||||
|
@ -1,13 +1,23 @@
|
||||
import React from "react";
|
||||
import React, {useEffect} from "react";
|
||||
import {GameComponent} from "../components/gameComponent";
|
||||
import {useLocation} from "react-router-dom";
|
||||
import Player from "../game/player";
|
||||
import {useAtom} from "jotai";
|
||||
import {thisPlayerAtom} from "../utils/state";
|
||||
|
||||
const Game: Component = () => {
|
||||
const location = useLocation();
|
||||
const player = location.state as Player;
|
||||
const [player] = useAtom(thisPlayerAtom);
|
||||
|
||||
return <GameComponent player={player}/>;
|
||||
useEffect(() => {
|
||||
if (!player) {
|
||||
// TODO state dissapears on refresh
|
||||
window.location.href = "/";
|
||||
}
|
||||
}, []);
|
||||
|
||||
if (player) {
|
||||
return <GameComponent player={player}/>;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export default Game;
|
||||
|
@ -4,11 +4,14 @@ import Input from "../components/input";
|
||||
import Dropdown from "../components/dropdown";
|
||||
import {Colour, getColours} from "../game/colour";
|
||||
import {useNavigate} from "react-router-dom";
|
||||
import {useAtom} from "jotai";
|
||||
import {thisPlayerAtom} from "../utils/state";
|
||||
|
||||
const Home: Component = () => {
|
||||
|
||||
const input = useRef<HTMLInputElement>(null);
|
||||
const dropdown = useRef<HTMLSelectElement>(null);
|
||||
const [, setPlayer] = useAtom(thisPlayerAtom);
|
||||
const navigate = useNavigate();
|
||||
|
||||
function formHandler(): void {
|
||||
@ -17,7 +20,8 @@ const Home: Component = () => {
|
||||
Name: input.current.value,
|
||||
Colour: dropdown.current.value as Colour,
|
||||
});
|
||||
navigate("/game", {state: player});
|
||||
setPlayer(player);
|
||||
navigate("/game");
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -1,7 +1,10 @@
|
||||
import Player from "../game/player";
|
||||
import {Character, PacMan} from "../game/character";
|
||||
import {Character, Ghost, PacMan} from "../game/character";
|
||||
import {testMap} from "../game/map";
|
||||
import {TileType} from "../game/tileType";
|
||||
import {getDefaultStore} from "jotai";
|
||||
import {charactersAtom, currentPlayerAtom, diceAtom, playersAtom} from "./state";
|
||||
import {Colour} from "../game/colour";
|
||||
|
||||
export enum GameAction {
|
||||
rollDice,
|
||||
@ -10,33 +13,40 @@ export enum GameAction {
|
||||
ready,
|
||||
}
|
||||
|
||||
function doAction(message: MessageEvent<string>): void { // TODO Jotai state management?
|
||||
const ghosts = [
|
||||
new Ghost({Colour: Colour.Purple}),
|
||||
new Ghost({Colour: Colour.Purple}),
|
||||
];
|
||||
|
||||
const store = getDefaultStore();
|
||||
|
||||
export function doAction(message: MessageEvent<string>): void { // TODO divide into smaller functions
|
||||
const parsed: ActionMessage = JSON.parse(message.data);
|
||||
|
||||
switch (parsed.Action) {
|
||||
case GameAction.rollDice:
|
||||
setDice(parsed.Data as number[]);
|
||||
store.set(diceAtom, parsed.Data as number[]);
|
||||
break;
|
||||
case GameAction.moveCharacter:
|
||||
setDice(parsed.Data?.dice as number[]);
|
||||
store.set(diceAtom, parsed.Data?.dice as number[]);
|
||||
updateCharacters(parsed);
|
||||
removeEatenPellets(parsed);
|
||||
break;
|
||||
case GameAction.playerInfo:
|
||||
const playerProps = parsed.Data as PlayerProps[];
|
||||
console.log(playerProps);
|
||||
setPlayers(playerProps.map(p => new Player(p)));
|
||||
store.set(playersAtom, playerProps.map(p => new Player(p)));
|
||||
const pacMen = playerProps.filter(p => p.PacMan).map(p => new PacMan(p.PacMan!));
|
||||
console.log(pacMen);
|
||||
// TODO find spawn points
|
||||
setCharacters([...pacMen, ...ghosts]);
|
||||
store.set(charactersAtom, [...pacMen, ...ghosts]);
|
||||
break;
|
||||
case GameAction.ready:
|
||||
const isReady = parsed.Data.AllReady as boolean;
|
||||
if (isReady) {
|
||||
setCurrentPlayer(new Player(parsed.Data.Starter as PlayerProps));
|
||||
store.set(currentPlayerAtom, new Player(parsed.Data.Starter as PlayerProps));
|
||||
}
|
||||
setPlayers((parsed.Data.Players as PlayerProps[]).map(p => new Player(p)));
|
||||
store.set(playersAtom, (parsed.Data.Players as PlayerProps[]).map(p => new Player(p)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -57,6 +67,6 @@ function updateCharacters(parsed: ActionMessage): void {
|
||||
for (const character of updatedCharacters) {
|
||||
newList.push(new Character(character));
|
||||
}
|
||||
setCharacters(newList);
|
||||
store.set(charactersAtom, newList);
|
||||
}
|
||||
}
|
||||
|
11
pac-man-board-game/ClientApp/src/utils/state.ts
Normal file
11
pac-man-board-game/ClientApp/src/utils/state.ts
Normal file
@ -0,0 +1,11 @@
|
||||
// TODO merge character and player atoms, since the player is the owner of the character
|
||||
import Player from "../game/player";
|
||||
import {atom} from "jotai";
|
||||
import {Character} from "../game/character";
|
||||
|
||||
export const charactersAtom = atom<Character[] | undefined>(undefined);
|
||||
export const playersAtom = atom<Player[]>([]);
|
||||
export const thisPlayerAtom = atom<Player | undefined>(undefined);
|
||||
export const diceAtom = atom<number[] | undefined>(undefined);
|
||||
export const selectedDiceAtom = atom<SelectedDice | undefined>(undefined);
|
||||
export const currentPlayerAtom = atom<Player | undefined>(undefined);
|
Loading…
x
Reference in New Issue
Block a user