Fixed bugs, sending players instead of characters

This commit is contained in:
martin 2023-07-09 19:10:36 +02:00
parent c0a35fc298
commit 0cb4367999
6 changed files with 60 additions and 53 deletions

View File

@ -3,9 +3,10 @@ import {Character, PacMan} from "../game/character";
import findPossiblePositions from "../game/possibleMovesAlgorithm"; import findPossiblePositions from "../game/possibleMovesAlgorithm";
import {GameTile} from "./gameTile"; import {GameTile} from "./gameTile";
import {TileType} from "../game/tileType"; import {TileType} from "../game/tileType";
import {useAtomValue} from "jotai";
import {allCharactersAtom} from "../utils/state";
interface BoardProps extends ComponentProps { interface BoardProps extends ComponentProps {
characters: Character[],
selectedDice?: SelectedDice, selectedDice?: SelectedDice,
onMove?: Action<Position[]>, onMove?: Action<Position[]>,
map: GameMap map: GameMap
@ -14,12 +15,12 @@ interface BoardProps extends ComponentProps {
const Board: Component<BoardProps> = ( const Board: Component<BoardProps> = (
{ {
className, className,
characters,
selectedDice, selectedDice,
onMove, onMove,
map map
}) => { }) => {
const characters = useAtomValue(allCharactersAtom);
const [selectedCharacter, setSelectedCharacter] = useState<Character>(); const [selectedCharacter, setSelectedCharacter] = useState<Character>();
const [possiblePositions, setPossiblePositions] = useState<Path[]>([]); // TODO reset when other client moves a character const [possiblePositions, setPossiblePositions] = useState<Path[]>([]); // TODO reset when other client moves a character
const [hoveredPosition, setHoveredPosition] = useState<Path>(); const [hoveredPosition, setHoveredPosition] = useState<Path>();

View File

@ -6,19 +6,17 @@ import WebSocketService from "../websockets/WebSocketService";
import {testMap} from "../game/map"; import {testMap} from "../game/map";
import Player, {State} from "../game/player"; import Player, {State} from "../game/player";
import PlayerStats from "../components/playerStats"; import PlayerStats from "../components/playerStats";
import {useAtom} from "jotai"; import {getDefaultStore, useAtom, useAtomValue} from "jotai";
import {charactersAtom, currentPlayerAtom, diceAtom, playersAtom, selectedDiceAtom} from "../utils/state"; import {currentPlayerAtom, diceAtom, ghostsAtom, playersAtom, selectedDiceAtom} from "../utils/state";
const wsService = new WebSocketService(import.meta.env.VITE_API); const wsService = new WebSocketService(import.meta.env.VITE_API);
export const GameComponent: Component<{ player: Player }> = ({player}) => { export const GameComponent: Component<{ player: Player }> = ({player}) => {
// TODO find spawn points const players = useAtomValue(playersAtom);
const [characters] = useAtom(charactersAtom);
const [players] = useAtom(playersAtom);
const [dice] = useAtom(diceAtom); const dice = useAtomValue(diceAtom);
const [selectedDice, setSelectedDice] = useAtom(selectedDiceAtom); const [selectedDice, setSelectedDice] = useAtom(selectedDiceAtom);
const [currentPlayer] = useAtom(currentPlayerAtom); const currentPlayer = useAtomValue(currentPlayerAtom);
function startGameLoop(): void { function startGameLoop(): void {
if (currentPlayer?.Name !== player.Name) return; if (currentPlayer?.Name !== player.Name) return;
@ -35,9 +33,10 @@ export const GameComponent: Component<{ player: Player }> = ({player}) => {
const data: ActionMessage = { const data: ActionMessage = {
Action: GameAction.moveCharacter, Action: GameAction.moveCharacter,
Data: { Data: {
dice: dice?.length ?? 0 > 0 ? dice : null, Dice: dice?.length ?? 0 > 0 ? dice : null,
characters: characters, Players: players,
eatenPellets: eatenPellets Ghosts: getDefaultStore().get(ghostsAtom),
EatenPellets: eatenPellets
} }
}; };
wsService.send(data); wsService.send(data);
@ -70,13 +69,10 @@ export const GameComponent: Component<{ player: Player }> = ({player}) => {
} }
</div> </div>
<AllDice values={dice} selectedDiceIndex={selectedDice?.index}/> <AllDice values={dice} selectedDiceIndex={selectedDice?.index}/>
{players?.map(p => <PlayerStats key={p.Name} player={p} isCurrentPlayer={currentPlayer === p}/>)} {players?.map(p => <PlayerStats key={p.Name} player={p} isCurrentPlayer={currentPlayer?.Name === p.Name}/>)}
{characters && <GameBoard className={"mx-auto my-2"}
<GameBoard className={"mx-auto my-2"} selectedDice={selectedDice}
characters={characters} onMove={onCharacterMove} map={testMap}/>
selectedDice={selectedDice}
onMove={onCharacterMove} map={testMap}/>
}
</> </>
); );
}; };

View File

@ -34,7 +34,7 @@ interface BoxProps {
interface PlayerProps { interface PlayerProps {
readonly Name: string, readonly Name: string,
readonly PacMan?: CharacterProps, readonly PacMan: CharacterProps,
readonly Colour: import("../game/colour").Colour, readonly Colour: import("../game/colour").Colour,
readonly Box?: BoxProps, readonly Box?: BoxProps,
State?: import("../game/player").State, State?: import("../game/player").State,

View File

@ -1,9 +1,9 @@
import Player from "../game/player"; import Player from "../game/player";
import {Character, CharacterType, Ghost, PacMan} from "../game/character"; import {CharacterType, Ghost} from "../game/character";
import {getCharacterSpawns, testMap} from "../game/map"; import {getCharacterSpawns, testMap} from "../game/map";
import {TileType} from "../game/tileType"; import {TileType} from "../game/tileType";
import {getDefaultStore} from "jotai"; import {getDefaultStore} from "jotai";
import {charactersAtom, currentPlayerAtom, diceAtom, playersAtom} from "./state"; import {currentPlayerAtom, diceAtom, ghostsAtom, playersAtom} from "./state";
import {Colour} from "../game/colour"; import {Colour} from "../game/colour";
export enum GameAction { export enum GameAction {
@ -13,19 +13,20 @@ export enum GameAction {
ready, ready,
} }
const store = getDefaultStore();
const ghostsProps: CharacterProps[] = [ const ghostsProps: CharacterProps[] = [
{Colour: Colour.Purple}, {Colour: Colour.Purple},
{Colour: Colour.Purple}, {Colour: Colour.Purple},
]; ];
let spawns = getCharacterSpawns(testMap).filter(spawn => spawn.type === CharacterType.ghost); let spawns = getCharacterSpawns(testMap).filter(spawn => spawn.type === CharacterType.ghost);
ghostsProps.forEach(ghost => { ghostsProps.forEach(ghost => {
ghost.SpawnPosition = spawns.pop()?.position; ghost.SpawnPosition = spawns.pop()?.position;
});
});
const ghosts = ghostsProps.map(props => new Ghost(props)); const ghosts = ghostsProps.map(props => new Ghost(props));
const store = getDefaultStore(); store.set(ghostsAtom, ghosts);
export const doAction: MessageEventFunction<string> = (event): void => { // TODO divide into smaller functions export const doAction: MessageEventFunction<string> = (event): void => { // TODO divide into smaller functions
const message: ActionMessage = JSON.parse(event.data); const message: ActionMessage = JSON.parse(event.data);
@ -51,28 +52,41 @@ function setDice(data?: number[]): void {
store.set(diceAtom, data); store.set(diceAtom, data);
} }
type MoveCharacterData = { dice: number[], characters: CharacterProps[], eatenPellets: Position[] }; type MoveCharacterData = { Dice: number[], Players: PlayerProps[], Ghosts: CharacterProps[], EatenPellets: Position[] };
function moveCharacter(data?: MoveCharacterData): void { function moveCharacter(data?: MoveCharacterData): void {
store.set(diceAtom, data?.dice); store.set(diceAtom, data?.Dice);
updateCharacters(data); updatePlayers(data);
updateGhosts(data);
removeEatenPellets(data); removeEatenPellets(data);
} }
function updateCharacters(data?: MoveCharacterData): void { function updatePlayers(data?: MoveCharacterData): void {
const updatedCharacters = data?.characters; const updatedPlayers = data?.Players;
if (updatedCharacters) { if (updatedPlayers) {
const newList: Character[] = []; const newList: Player[] = [];
for (const character of updatedCharacters) { for (const player of updatedPlayers) {
newList.push(new Character(character)); newList.push(new Player(player));
} }
store.set(charactersAtom, newList); store.set(playersAtom, newList);
}
}
function updateGhosts(data?: MoveCharacterData): void {
const updatedGhosts = data?.Ghosts;
if (updatedGhosts) {
const newList: Ghost[] = [];
for (const ghost of updatedGhosts) {
newList.push(new Ghost(ghost));
}
store.set(ghostsAtom, newList);
} }
} }
function removeEatenPellets(data?: MoveCharacterData): void { function removeEatenPellets(data?: MoveCharacterData): void {
const pellets = data?.eatenPellets; const pellets = data?.EatenPellets;
for (const pellet of pellets ?? []) { for (const pellet of pellets ?? []) {
testMap[pellet.y][pellet.x] = TileType.empty; testMap[pellet.y][pellet.x] = TileType.empty;
@ -81,20 +95,15 @@ function removeEatenPellets(data?: MoveCharacterData): void {
function playerInfo(data?: PlayerProps[]): void { function playerInfo(data?: PlayerProps[]): void {
const playerProps = data ?? []; const playerProps = data ?? [];
store.set(playersAtom, playerProps.map(p => new Player(p)));
spawns = getCharacterSpawns(testMap).filter(spawn => spawn.type === CharacterType.pacMan); spawns = getCharacterSpawns(testMap).filter(spawn => spawn.type === CharacterType.pacMan);
const pacMen = playerProps.filter(p => p.PacMan).map(p => { store.set(playersAtom, playerProps.map(p => {
if (!p.PacMan!.SpawnPosition) { if (!p.PacMan.SpawnPosition) {
p.PacMan!.SpawnPosition = spawns.pop()?.position; p.PacMan.SpawnPosition = spawns.pop()?.position;
} }
return new PacMan(p.PacMan!); return new Player(p);
}); }));
store.set(charactersAtom, [...pacMen, ...ghosts]);
} }
type ReadyData = type ReadyData =
| { AllReady: true, Starter: PlayerProps, Players: PlayerProps[] } | { AllReady: true, Starter: PlayerProps, Players: PlayerProps[] }
| { AllReady: false, Players: PlayerProps[] } | { AllReady: false, Players: PlayerProps[] }
@ -102,8 +111,7 @@ type ReadyData =
function ready(data?: ReadyData): void { function ready(data?: ReadyData): void {
if (data && typeof data !== "string") { if (data && typeof data !== "string") {
const isReady = data.AllReady; if (data.AllReady) {
if (isReady) {
store.set(currentPlayerAtom, new Player(data.Starter)); store.set(currentPlayerAtom, new Player(data.Starter));
} }
store.set(playersAtom, data.Players.map(p => new Player(p))); store.set(playersAtom, data.Players.map(p => new Player(p)));

View File

@ -1,13 +1,15 @@
import Player from "../game/player"; import Player from "../game/player";
import {atom} from "jotai"; import {atom} from "jotai";
import {Character} from "../game/character";
import {atomWithStorage, createJSONStorage} from "jotai/utils"; import {atomWithStorage, createJSONStorage} from "jotai/utils";
import {Ghost} from "../game/character";
const playerStorage = createJSONStorage<Player | undefined>(() => sessionStorage); const playerStorage = createJSONStorage<Player | undefined>(() => sessionStorage);
// TODO merge character and player atoms, since the player is the owner of the character // TODO derived from playersAtom
export const charactersAtom = atom<Character[] | undefined>(undefined);
export const playersAtom = atom<Player[]>([]); export const playersAtom = atom<Player[]>([]);
export const playerCharactersAtom = atom(get => get(playersAtom).map(player => player.PacMan));
export const ghostsAtom = atom<Ghost[]>([]);
export const allCharactersAtom = atom(get => [...get(playerCharactersAtom), ...get(ghostsAtom)]);
export const thisPlayerAtom = atomWithStorage<Player | undefined>("player", undefined, { export const thisPlayerAtom = atomWithStorage<Player | undefined>("player", undefined, {
...playerStorage, ...playerStorage,
getItem(key, initialValue): Player | undefined { getItem(key, initialValue): Player | undefined {

View File

@ -11,12 +11,12 @@ public class GameGroup // TODO tests
public IPlayer RandomPlayer => Players[_random.Next(Players.Count)]; public IPlayer RandomPlayer => Players[_random.Next(Players.Count)];
public event Func<ArraySegment<byte>, Task>? Connections; public event Func<ArraySegment<byte>, Task>? Connections;
public bool AddPlayer(IPlayer player) public bool AddPlayer(IPlayer player) // TODO if name exists, use that player instead
{ {
if (Players.Count >= Rules.MaxPlayers) return false; if (Players.Count >= Rules.MaxPlayers) return false;
if (Players.Exists(p => p.Name == player.Name)) return false;
player.State = State.WaitingForPlayers; player.State = State.WaitingForPlayers;
if (Players.Exists(p => p.Name == player.Name)) return true;
Players.Add(player); Players.Add(player);
return true; return true;
} }