Score and pellets should update on other clients
This commit is contained in:
parent
2543581211
commit
63c531635e
@ -4,12 +4,12 @@ import findPossiblePositions from "../game/possibleMovesAlgorithm";
|
||||
import {Direction} from "../game/direction";
|
||||
import {GameTile} from "./gameTile";
|
||||
import {TileType} from "../game/tileType";
|
||||
import {NormalPellet, PowerPellet} from "../game/pellet";
|
||||
import Pellet from "../game/pellet";
|
||||
|
||||
interface BoardProps extends ComponentProps {
|
||||
characters: Character[],
|
||||
selectedDice?: SelectedDice,
|
||||
onMove?: Action<Character>,
|
||||
onMove?: BiAction<Character, Position[]>,
|
||||
map: GameMap
|
||||
}
|
||||
|
||||
@ -38,28 +38,33 @@ const Board: Component<BoardProps> = (
|
||||
if (selectedCharacter) {
|
||||
setHoveredPosition(undefined);
|
||||
selectedCharacter.follow(destination);
|
||||
|
||||
pickUpPellets(destination);
|
||||
onMove?.(selectedCharacter);
|
||||
|
||||
const positions = pickUpPellets(destination);
|
||||
onMove?.(selectedCharacter, positions);
|
||||
setSelectedCharacter(undefined);
|
||||
}
|
||||
}
|
||||
|
||||
function pickUpPellets(destination: Path): void {
|
||||
function pickUpPellets(destination: Path): Position[] {
|
||||
const positions: Position[] = [];
|
||||
if (selectedCharacter instanceof PacMan) {
|
||||
const pacMan = selectedCharacter as PacMan;
|
||||
|
||||
for (const tile of [...destination.path ?? [], destination.end]) {
|
||||
const currentTile = map[tile.y][tile.x];
|
||||
|
||||
if (currentTile === TileType.pellet) {
|
||||
pacMan.box.addPellet(new NormalPellet());
|
||||
pacMan.box.addPellet(new Pellet());
|
||||
map[tile.y][tile.x] = TileType.empty;
|
||||
positions.push(tile);
|
||||
} else if (currentTile === TileType.powerPellet) {
|
||||
pacMan.box.addPellet(new PowerPellet());
|
||||
pacMan.box.addPellet(new Pellet(true));
|
||||
map[tile.y][tile.x] = TileType.empty;
|
||||
positions.push(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
return positions;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
|
@ -6,15 +6,19 @@ import {Character, Ghost, PacMan} from "../game/character";
|
||||
import WebSocketService from "../websockets/WebSocketService";
|
||||
import {testMap} from "../game/map";
|
||||
import {Direction} from "../game/direction";
|
||||
import Box from "../game/box";
|
||||
import {TileType} from "../game/tileType";
|
||||
|
||||
const wsService = new WebSocketService("wss://localhost:3000/api/game");
|
||||
|
||||
export const GameComponent: Component = () => {
|
||||
// TODO find spawn points
|
||||
const [characters, setCharacters] = useState([
|
||||
new PacMan("yellow", {at: {x: 3, y: 3}, direction: Direction.up}),
|
||||
new Ghost("purple", {at: {x: 8, y: 3}, direction: Direction.up})
|
||||
new PacMan({
|
||||
colour: "yellow", spawnPosition: {at: {x: 3, y: 3}, direction: Direction.up}
|
||||
}),
|
||||
new Ghost({
|
||||
colour: "purple", spawnPosition: {at: {x: 8, y: 3}, direction: Direction.up}
|
||||
})
|
||||
]);
|
||||
|
||||
const [dice, setDice] = useState<number[]>();
|
||||
@ -29,11 +33,11 @@ export const GameComponent: Component = () => {
|
||||
}
|
||||
|
||||
function startGameLoop(): void {
|
||||
setSelectedDice(undefined);
|
||||
if (!wsService.isOpen()) {
|
||||
setTimeout(startGameLoop, 50);
|
||||
return;
|
||||
}
|
||||
setSelectedDice(undefined);
|
||||
rollDice();
|
||||
}
|
||||
|
||||
@ -46,23 +50,34 @@ export const GameComponent: Component = () => {
|
||||
break;
|
||||
case GameAction.moveCharacter:
|
||||
setDice(parsed.Data?.dice as number[]);
|
||||
const character = parsed.Data?.character satisfies Ghost | PacMan;
|
||||
const currentCharacter = characters.find(c => c.color === character.color);
|
||||
|
||||
if (currentCharacter) {
|
||||
currentCharacter.position = character.position;
|
||||
}
|
||||
|
||||
// TODO update pellets on other clients (character and on map)
|
||||
// if (character satisfies PacMan) {
|
||||
// (characters[currentCharacter] as PacMan).box = new Box(character.box.colour, character.box.pellets);
|
||||
// console.log(characters[currentCharacter]);
|
||||
// }
|
||||
updateCharacter(parsed);
|
||||
removeEatenPellets(parsed);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function onCharacterMove(character: Character): void {
|
||||
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 updateCharacter(parsed: ActionMessage): void {
|
||||
const updatedCharacter = parsed.Data?.character satisfies Ghost | PacMan;
|
||||
const characterIndex = characters.findIndex(c => c.colour === updatedCharacter.colour);
|
||||
|
||||
if (characters[characterIndex]) {
|
||||
if (updatedCharacter satisfies PacMan) {
|
||||
(characters[characterIndex] as PacMan) = new PacMan(updatedCharacter);
|
||||
} else if (updatedCharacter satisfies Ghost) {
|
||||
(characters[characterIndex] as Ghost) = new Ghost(updatedCharacter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onCharacterMove(character: Character, eatenPellets: Position[]): void {
|
||||
if (dice && selectedDice) {
|
||||
dice.splice(selectedDice.index, 1);
|
||||
}
|
||||
@ -71,7 +86,8 @@ export const GameComponent: Component = () => {
|
||||
Action: GameAction.moveCharacter,
|
||||
Data: {
|
||||
dice: dice?.length ?? 0 > 0 ? dice : null,
|
||||
character: character
|
||||
character: character,
|
||||
eatenPellets: eatenPellets
|
||||
}
|
||||
};
|
||||
wsService.send(data);
|
||||
@ -94,8 +110,8 @@ export const GameComponent: Component = () => {
|
||||
<AllDice values={dice} onclick={handleDiceClick} selectedDiceIndex={selectedDice?.index}/>
|
||||
{
|
||||
(characters.filter(c => c instanceof PacMan) as PacMan[]).map(c =>
|
||||
<div key={c.color} className={"mx-auto w-fit m-2"}>
|
||||
<p>Pac-Man: {c.color}</p>
|
||||
<div key={c.colour} className={"mx-auto w-fit m-2"}>
|
||||
<p>Pac-Man: {c.colour}</p>
|
||||
<p>Pellets: {c.box.count}</p>
|
||||
<p>PowerPellets: {c.box.countPowerPellets}</p>
|
||||
</div>)
|
||||
|
@ -35,7 +35,7 @@ export const GameTile: Component<TileWithCharacterProps> = (
|
||||
onMouseLeave={handleStopShowPath}>
|
||||
<>
|
||||
{character &&
|
||||
<div className={"flex-center w-full h-full"}>
|
||||
<div className={"flex-center wh-full"}>
|
||||
<CharacterComponent
|
||||
character={character}
|
||||
onClick={handleSelectCharacter}
|
||||
@ -60,11 +60,11 @@ const Circle: Component<CircleProps> = ({colour = "white"}) => (
|
||||
|
||||
interface TileProps extends ChildProps {
|
||||
type?: TileType,
|
||||
onClick?: () => void,
|
||||
onMouseEnter?: () => void,
|
||||
onMouseLeave?: () => void,
|
||||
onClick?: VoidFunction,
|
||||
onMouseEnter?: VoidFunction,
|
||||
onMouseLeave?: VoidFunction,
|
||||
character?: Character,
|
||||
onCharacterClick?: (character: Character) => void,
|
||||
onCharacterClick?: Action<Character>,
|
||||
characterClass?: string,
|
||||
}
|
||||
|
||||
@ -112,9 +112,9 @@ const Tile: Component<TileProps> = (
|
||||
onClick={onClick}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}>
|
||||
{children}
|
||||
{type === TileType.pellet && <Circle colour={"yellow"}/>}
|
||||
{type === TileType.powerPellet && <Circle colour={"red"}/>}
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@ -126,8 +126,8 @@ interface AddDummyProps extends ComponentProps {
|
||||
const AddDummy: Component<AddDummyProps> = ({path}) => (
|
||||
<>
|
||||
{path &&
|
||||
<div className={"flex-center w-full h-full"}>
|
||||
<CharacterComponent character={new Dummy({at: path.end, direction: path.direction})}/>
|
||||
<div className={"flex-center wh-full"}>
|
||||
<CharacterComponent character={new Dummy(path)}/>
|
||||
</div>
|
||||
}
|
||||
</>
|
||||
@ -135,7 +135,7 @@ const AddDummy: Component<AddDummyProps> = ({path}) => (
|
||||
|
||||
interface CharacterComponentProps extends ComponentProps {
|
||||
character?: Character,
|
||||
onClick?: (character: Character) => void,
|
||||
onClick?: Action<Character>,
|
||||
}
|
||||
|
||||
const CharacterComponent: Component<CharacterComponentProps> = (
|
||||
@ -162,7 +162,7 @@ const CharacterComponent: Component<CharacterComponentProps> = (
|
||||
|
||||
return (
|
||||
<div className={`rounded-full w-4/5 h-4/5 cursor-pointer hover:border border-black relative ${className}`}
|
||||
style={{backgroundColor: `${character.color}`}}
|
||||
style={{backgroundColor: `${character.colour}`}}
|
||||
onClick={() => onClick?.(character)}>
|
||||
<div>
|
||||
<div className={`absolute ${getSide()} w-1/2 h-1/2 rounded-full bg-black`}/>
|
||||
|
@ -1,10 +1,15 @@
|
||||
import {NormalPellet, Pellet, PowerPellet} from "./pellet";
|
||||
import Pellet from "./pellet";
|
||||
|
||||
export interface BoxProps {
|
||||
pellets?: Pellet[],
|
||||
readonly colour: Colour,
|
||||
}
|
||||
|
||||
export default class Box {
|
||||
private pellets: Pellet[] = [];
|
||||
public pellets: Pellet[];
|
||||
public readonly colour: Colour;
|
||||
|
||||
public constructor(colour: Colour, pellets: Pellet[] = []) {
|
||||
public constructor({colour, pellets = []}: BoxProps) {
|
||||
this.colour = colour;
|
||||
this.pellets = pellets;
|
||||
}
|
||||
@ -14,15 +19,15 @@ export default class Box {
|
||||
}
|
||||
|
||||
get powerPellet(): Pellet | undefined {
|
||||
return this.pellets.find(pellet => pellet instanceof PowerPellet);
|
||||
return this.pellets.find(pellet => pellet.isPowerPellet);
|
||||
}
|
||||
|
||||
get count(): number {
|
||||
return this.pellets.filter(pellet => pellet instanceof NormalPellet).length;
|
||||
return this.pellets.filter(pellet => !pellet.isPowerPellet).length;
|
||||
}
|
||||
|
||||
|
||||
get countPowerPellets(): number {
|
||||
return this.pellets.filter(pellet => pellet instanceof PowerPellet).length;
|
||||
return this.pellets.filter(pellet => pellet.isPowerPellet).length;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,14 +1,23 @@
|
||||
import Box from "./box";
|
||||
import Box, {BoxProps} from "./box";
|
||||
import {Direction} from "./direction";
|
||||
|
||||
interface CharacterProps {
|
||||
colour: Colour,
|
||||
position?: Path,
|
||||
isEatable?: boolean,
|
||||
spawnPosition: DirectionalPosition
|
||||
}
|
||||
|
||||
export abstract class Character {
|
||||
public color: Colour;
|
||||
public readonly colour: Colour;
|
||||
public position: Path;
|
||||
public isEatable = false;
|
||||
public isEatable: boolean;
|
||||
public readonly spawnPosition: DirectionalPosition;
|
||||
|
||||
protected constructor(color: Colour, spawnPosition: DirectionalPosition) {
|
||||
this.color = color;
|
||||
this.position = {end: spawnPosition.at, direction: spawnPosition.direction};
|
||||
protected constructor({colour, position, isEatable = false, spawnPosition}: CharacterProps) {
|
||||
this.colour = colour;
|
||||
this.position = position ?? {end: spawnPosition.at, direction: spawnPosition.direction};
|
||||
this.isEatable = isEatable;
|
||||
this.spawnPosition = spawnPosition;
|
||||
}
|
||||
|
||||
@ -18,34 +27,46 @@ export abstract class Character {
|
||||
this.position.path = undefined;
|
||||
}
|
||||
|
||||
public moveToSpawn(): void {
|
||||
this.follow({end: this.spawnPosition.at, direction: this.spawnPosition.direction});
|
||||
}
|
||||
|
||||
public isAt(position: Position): boolean {
|
||||
return this.position.end.x === position.x && this.position.end.y === position.y;
|
||||
}
|
||||
}
|
||||
|
||||
interface PacManProps extends CharacterProps {
|
||||
box?: BoxProps,
|
||||
}
|
||||
|
||||
export class PacMan extends Character {
|
||||
|
||||
public box: Box;
|
||||
|
||||
public constructor(color: Colour, spawnPosition: DirectionalPosition) {
|
||||
super(color, spawnPosition);
|
||||
this.isEatable = true;
|
||||
this.box = new Box(color);
|
||||
public constructor({colour, position, isEatable = true, spawnPosition, box = {colour}}: PacManProps) {
|
||||
super({colour, position, isEatable, spawnPosition});
|
||||
this.isEatable = isEatable;
|
||||
this.box = new Box(box);
|
||||
}
|
||||
|
||||
public stealFrom(other: PacMan): void {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class Ghost extends Character {
|
||||
|
||||
public constructor(color: Colour, spawnPosition: DirectionalPosition) {
|
||||
super(color, spawnPosition);
|
||||
public constructor({colour, position, isEatable, spawnPosition}: CharacterProps) {
|
||||
super({colour, position, isEatable, spawnPosition});
|
||||
}
|
||||
}
|
||||
|
||||
export class Dummy extends Character {
|
||||
|
||||
public constructor(position: DirectionalPosition) {
|
||||
super("grey", position);
|
||||
public constructor(position: Path) { // TODO see-through
|
||||
super({colour: "grey", position, isEatable: false, spawnPosition: {at: {x: 0, y: 0}, direction: Direction.up}});
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,20 +1,7 @@
|
||||
export abstract class Pellet {
|
||||
public readonly colour: Colour;
|
||||
export default class Pellet {
|
||||
public readonly isPowerPellet: boolean;
|
||||
|
||||
protected constructor(colour: Colour) {
|
||||
this.colour = colour;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class NormalPellet extends Pellet {
|
||||
public constructor() {
|
||||
super("white");
|
||||
}
|
||||
}
|
||||
|
||||
export class PowerPellet extends Pellet {
|
||||
public constructor() {
|
||||
super("yellow");
|
||||
public constructor(isPowerPellet = false) {
|
||||
this.isPowerPellet = isPowerPellet;
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,10 @@
|
||||
@apply flex justify-center items-center;
|
||||
}
|
||||
|
||||
.wh-full {
|
||||
@apply w-full h-full;
|
||||
}
|
||||
|
||||
h1 {
|
||||
@apply text-4xl;
|
||||
}
|
||||
|
@ -11,6 +11,8 @@ type ActionMessage<T = any> = {
|
||||
|
||||
type Action<T> = (obj: T) => void;
|
||||
|
||||
type BiAction<T1, T2> = (obj1: T1, obj2: T2) => void;
|
||||
|
||||
type SelectedDice = {
|
||||
value: number,
|
||||
index: number
|
||||
|
Loading…
x
Reference in New Issue
Block a user