summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
Diffstat (limited to 'client')
-rw-r--r--client/src/RemotePlayerEngine.ts76
-rw-r--r--client/src/RemotePlayerSprite.ts137
-rw-r--r--client/src/client.ts32
-rw-r--r--client/src/events.ts12
-rw-r--r--client/src/scene.ts22
-rw-r--r--client/src/socket.ts8
-rw-r--r--client/src/sprite.ts8
-rw-r--r--client/src/uiScene.ts17
8 files changed, 273 insertions, 39 deletions
diff --git a/client/src/RemotePlayerEngine.ts b/client/src/RemotePlayerEngine.ts
index f19ebe0..5d85f24 100644
--- a/client/src/RemotePlayerEngine.ts
+++ b/client/src/RemotePlayerEngine.ts
@@ -1,14 +1,84 @@
import Phaser from 'phaser';
+import type AkkamonStartScene from './scene';
import { akkamonClient } from './app';
+import type { Direction } from './Direction';
+import {
+ Queue,
+ RemotePlayerSprite
+} from './RemotePlayerSprite';
+import type {
+ RemoteMovementQueues
+} from './events';
export class RemotePlayerEngine {
- private scene: Phaser.Scene
+ private scene: AkkamonStartScene;
- constructor(scene: Phaser.Scene) {
+ private trainerIdToRemotePlayerSprite: Map<string, RemotePlayerSprite> = new Map();
+
+ constructor(scene: AkkamonStartScene) {
this.scene = scene;
}
- update() {
+ push(remoteMovementQueues: RemoteMovementQueues) {
+ this.updateMembers(remoteMovementQueues);
+ this.pushMovesToSprites(remoteMovementQueues);
+ }
+
+ pushMovesToSprites(remoteMovementQueues: RemoteMovementQueues) {
+ this.trainerIdToRemotePlayerSprite.forEach((remoteSprite: RemotePlayerSprite, key: string) => {
+ remoteSprite.push(remoteMovementQueues[key].value);
+ });
+ }
+
+ update(delta: number): void {
+ this.trainerIdToRemotePlayerSprite.forEach((remoteSprite: RemotePlayerSprite, key: string) => {
+ if (remoteSprite.isMoving()) {
+ console.log("remote player currently walking");
+ remoteSprite.updatePixelPosition(delta);
+ } else if (remoteSprite.hasMovesLeft()) {
+ console.log("remote player starts moving");
+ remoteSprite.startMoving();
+ }
+ });
+ }
+
+ updateMembers(newRemoteMovementQueues: RemoteMovementQueues) {
+ const traineridToQueueValue = newRemoteMovementQueues;
+
+ Object.keys(newRemoteMovementQueues).forEach((key: string) => {
+
+ var moveQueue = traineridToQueueValue[key].value;
+ if (moveQueue !== undefined) {
+
+ // console.log("-> key: " + key + " has position " + newTilePos.x + ", " + newTilePos.y);
+
+ if (!this.trainerIdToRemotePlayerSprite.has(key)) {
+ // console.log("adding remote player sprite for " + key);
+ this.trainerIdToRemotePlayerSprite.set(key,
+ new RemotePlayerSprite({
+ scene: this.scene,
+ tilePos: new Phaser.Math.Vector2(this.scene.spawnPointTilePos!),
+ texture: this.scene.textures.get("atlas"),
+ frame: "misa-front",
+ moveQueue: new Queue(moveQueue)
+ }
+ ));
+ } else {
+ // console.log("key: " + key + " already had a sprite!");
+ }
+ }
+
+ });
+
+ this.trainerIdToRemotePlayerSprite.forEach((value: RemotePlayerSprite, key: string) => {
+ if (!(key in newRemoteMovementQueues)) {
+ // console.log("removing remote player sprite for " + key);
+ this.trainerIdToRemotePlayerSprite.get(key)!.destroy();
+ this.trainerIdToRemotePlayerSprite.delete(key);
+ } else {
+ // console.log("Player " + key + " was not removed!");
+ }
+ });
}
}
diff --git a/client/src/RemotePlayerSprite.ts b/client/src/RemotePlayerSprite.ts
index 8485803..6103d09 100644
--- a/client/src/RemotePlayerSprite.ts
+++ b/client/src/RemotePlayerSprite.ts
@@ -1 +1,138 @@
import Phaser from 'phaser';
+import AkkamonStartScene from './scene';
+import { PlayerSprite } from './sprite';
+import { GridPhysics } from './GridPhysics';
+import { Direction } from './Direction';
+
+export class Queue<T> {
+ private _data = new Array();
+
+ constructor(data?: Array<T>) {
+ if (data !== undefined) {
+ this._data = data;
+ }
+ }
+
+ push(element: T): void {
+ this._data.push(element);
+ }
+
+ pushArray(arr: T[]): void {
+ for (var element of arr) {
+ this._data.push(element);
+ }
+ }
+
+ pop(): T | undefined {
+ return this._data.shift();
+ }
+
+ isEmpty(): boolean {
+ return this._data.length == 0;
+ }
+
+ peek() {
+ return this._data[0];
+ }
+}
+
+type RemotePlayerSpriteConfig = {
+ scene: Phaser.Scene,
+ tilePos: Phaser.Math.Vector2,
+ texture: Phaser.Textures.Texture | string,
+ frame?: string,
+ moveQueue: Queue<Direction>
+}
+
+export class RemotePlayerSprite extends PlayerSprite {
+
+ private lastTilePos?: Phaser.Math.Vector2;
+ private moveQueue: Queue<Direction> = new Queue();
+
+ private movementDirection: Direction = Direction.NONE;
+
+ private speedPixelsPerSecond: number = AkkamonStartScene.TILE_SIZE * 4;
+
+ private tileSizePixelsWalked: number = 0;
+
+ constructor(config: RemotePlayerSpriteConfig) {
+ super(config);
+ }
+
+ push(moveQueue: Array<Direction>): void {
+ for (var direction of moveQueue) {
+ if (direction !== Direction.NONE) {
+ this.moveQueue.push(direction);
+ }
+ }
+ // console.log(this.moveQueue);
+ }
+
+ updatePixelPosition(delta: number): void {
+ const pixelsToWalkThisUpdate = this.getPixelsToWalk(delta);
+
+ if (!this.willCrossTileBorderThisUpdate(pixelsToWalkThisUpdate)) {
+ this.move(pixelsToWalkThisUpdate);
+ } else if (this.shouldContinueMoving()) {
+ this.move(pixelsToWalkThisUpdate);
+ } else {
+ this.move(AkkamonStartScene.TILE_SIZE - this.tileSizePixelsWalked);
+ this.stopMoving();
+ }
+ }
+
+ shouldContinueMoving(): boolean {
+ if (this.moveQueue.peek() == this.movementDirection) {
+ console.log("continueing to move.");
+ this.moveQueue.pop();
+ return true;
+ }
+ return false;
+ }
+
+ willCrossTileBorderThisUpdate(pixelsToWalkThisUpdate: number): boolean {
+ return (this.tileSizePixelsWalked + pixelsToWalkThisUpdate) >= AkkamonStartScene.TILE_SIZE;
+ }
+
+ move(pixelsToMove: number): void {
+ this.tileSizePixelsWalked += pixelsToMove;
+ this.tileSizePixelsWalked %= AkkamonStartScene.TILE_SIZE;
+
+ const directionVec = GridPhysics.movementDirectionVectors[this.movementDirection]!.clone();
+
+ const moveVec = directionVec.multiply(
+ new Phaser.Math.Vector2(pixelsToMove)
+ );
+
+ const newPosition = this.getPosition().add(moveVec);
+ this.newPosition(newPosition);
+ }
+
+ getPixelsToWalk(delta: number): number {
+ const deltaInSeconds = delta / 1000;
+ return this.speedPixelsPerSecond * deltaInSeconds;
+ }
+
+ hasMovesLeft(): boolean {
+ return !this.moveQueue.isEmpty();
+ }
+
+ isMoving(): boolean {
+ return this.movementDirection !== Direction.NONE;
+ }
+
+ startMoving(): void {
+ if (!this.moveQueue.isEmpty()) {
+ this.movementDirection = this.moveQueue.pop()!;
+ this.startAnimation(this.movementDirection);
+ // console.log("remote player now walking in direction: " + this.movementDirection);
+ } else {
+ // console.log("moveQueue empty!");
+ }
+ }
+
+ stopMoving(): void {
+ this.stopAnimation(this.movementDirection);
+ this.movementDirection = Direction.NONE;
+ }
+}
diff --git a/client/src/client.ts b/client/src/client.ts
index 77d6676..f166c88 100644
--- a/client/src/client.ts
+++ b/client/src/client.ts
@@ -1,10 +1,13 @@
import type AkkamonSession from './session';
-import type { GameState } from './GameState';
import { Socket } from './socket';
+import type { GridPhysics } from './GridPhysics';
+import type { RemotePlayerEngine } from './RemotePlayerEngine';
+
import {
EventType,
HeartBeatReplyEvent,
- AkkamonEvent
+ IncomingEvent,
+ AkkamonEvent,
} from './events';
@@ -12,7 +15,8 @@ export class Client
{
private session: AkkamonSession;
- private akkamonState?: GameState;
+ private gridPhysics?: GridPhysics;
+ private remotePlayerEngine?: RemotePlayerEngine;
constructor(
url: string
@@ -20,18 +24,21 @@ export class Client
this.session = new Socket(url, this);
}
- getMutableState(): GameState {
- return this.akkamonState!;
- }
-
in(eventString: string) {
- let event: AkkamonEvent = JSON.parse(eventString);
- console.log("-> client is handling incoming event:");
- console.log(event);
+ let event: IncomingEvent = JSON.parse(eventString);
switch (event.type) {
case EventType.HEART_BEAT:
+ if (this.remotePlayerEngine !== undefined) {
+ this.remotePlayerEngine.push(event.remoteMovementQueues!);
+ }
this.send(new HeartBeatReplyEvent());
break;
+ case EventType.PLAYERS_NEARBY:
+ this.ui.setPlayersNearby();
+ break;
+ default:
+ console.log("ignored incoming event, doesn't match EventType interface.");
+ break;
}
}
@@ -42,4 +49,9 @@ export class Client
this.session.send(JSON.stringify(event));
}
}
+
+ setRemotePlayerEngine(engine: RemotePlayerEngine) {
+ this.remotePlayerEngine = engine;
+ }
+
}
diff --git a/client/src/events.ts b/client/src/events.ts
index cc001f7..1954fd7 100644
--- a/client/src/events.ts
+++ b/client/src/events.ts
@@ -1,6 +1,4 @@
import Phaser from 'phaser';
-import type { Player } from './player';
-import type { GameState } from './GameState';
import type { Direction } from './Direction';
export enum EventType {
@@ -15,6 +13,16 @@ export interface AkkamonEvent {
type: EventType
}
+export type TrainerPosition = { x: number, y: number }
+
+export type RemoteMovementQueues = {
+ [trainerId: string]: { value: Array<Direction> }
+}
+
+export interface IncomingEvent extends AkkamonEvent {
+ remoteMovementQueues?: RemoteMovementQueues
+}
+
export class PlayerRegistrationEvent implements AkkamonEvent {
public type: EventType = EventType.PLAYER_REGISTRATION;
diff --git a/client/src/scene.ts b/client/src/scene.ts
index 52edbdd..b6924d4 100644
--- a/client/src/scene.ts
+++ b/client/src/scene.ts
@@ -1,6 +1,4 @@
import Phaser from 'phaser';
-import { akkamonClient } from './app';
-import { Player } from './player';
import { PlayerSprite } from './sprite';
import { GridControls } from './GridControls';
@@ -9,10 +7,7 @@ import { Direction } from './Direction';
import { RemotePlayerEngine } from './RemotePlayerEngine';
-
-type RemotePlayerStates = {
- [name: string]: Player
-}
+import { akkamonClient } from './app';
export default class AkkamonStartScene extends Phaser.Scene
{
@@ -24,6 +19,11 @@ export default class AkkamonStartScene extends Phaser.Scene
private remotePlayerEngine?: RemotePlayerEngine
+ public spawnPointTilePos?: {
+ x: number,
+ y: number
+ };
+
directionToAnimation: {
[key in Direction]: string
} = {
@@ -34,7 +34,6 @@ export default class AkkamonStartScene extends Phaser.Scene
[Direction.NONE]: "misa-front-walk"
}
- remotePlayerSprites: {[name: string]: PlayerSprite} = {};
spawnPoint: Phaser.Types.Tilemaps.TiledObject | undefined;
@@ -88,19 +87,15 @@ export default class AkkamonStartScene extends Phaser.Scene
Math.floor(this.spawnPoint.x! / AkkamonStartScene.TILE_SIZE),
Math.floor(this.spawnPoint.y! / AkkamonStartScene.TILE_SIZE),
);
+ this.spawnPointTilePos = tilePos;
let player = new PlayerSprite({
scene: this,
tilePos: tilePos,
texture: this.textures.get("atlas"),
frame: "misa-front",
- player: new Player({
- trainerId: 'ash',
- position: tilePos
- })// this.akkamonState.getLocalMutablePlayerState(),
});
- this.add.existing(player);
this.gridPhysics = new GridPhysics(player, map);
this.gridControls = new GridControls(
this.input,
@@ -108,6 +103,7 @@ export default class AkkamonStartScene extends Phaser.Scene
);
this.remotePlayerEngine = new RemotePlayerEngine(this);
+ akkamonClient.setRemotePlayerEngine(this.remotePlayerEngine);
this.createPlayerAnimation(Direction.LEFT, 0, 3);
this.createPlayerAnimation(Direction.RIGHT, 0, 3);
@@ -120,12 +116,14 @@ export default class AkkamonStartScene extends Phaser.Scene
camera.roundPixels = true;
camera.setBounds(0, 0, map.widthInPixels, map.heightInPixels);
+ this.scene.launch('AkkamonUI');
}
update(time: number, delta: number) {
this.gridControls!.update();
this.gridPhysics!.update(delta);
+ this.remotePlayerEngine!.update(delta);
}
private createPlayerAnimation(direction: Direction, start: number, end: number) {
diff --git a/client/src/socket.ts b/client/src/socket.ts
index 8e2b9ef..4e83f89 100644
--- a/client/src/socket.ts
+++ b/client/src/socket.ts
@@ -14,17 +14,11 @@ export class Socket extends WebSocket implements AkkamonSession
super(url);
this.onopen = function echo(this: WebSocket, ev: Event) {
- console.log("opening socket");
- console.log("this is the websocket");
- console.log(this);
- console.log("logging in the session to the server");
+ console.log("Sending PlayerRegistrationEvent.");
client.send(new PlayerRegistrationEvent());
}
this.onmessage = function incomingMessage(this: WebSocket, ev: MessageEvent) {
- //console.log("received message from the server!");
- console.log("-> " + ev.data);
- // console.log("calling client.in:");
client.in(ev.data);
}
}
diff --git a/client/src/sprite.ts b/client/src/sprite.ts
index 960d374..948d8d9 100644
--- a/client/src/sprite.ts
+++ b/client/src/sprite.ts
@@ -1,6 +1,5 @@
import Phaser from 'phaser';
import AkkamonStartScene from './scene';
-import type { Player } from './player';
import type { Direction } from './Direction';
type PlayerSpriteConfig = {
@@ -8,16 +7,13 @@ type PlayerSpriteConfig = {
tilePos: Phaser.Math.Vector2,
texture: Phaser.Textures.Texture | string,
frame?: string,
- player: Player,
}
interface AkkamonPlayerSprite extends Phaser.GameObjects.Sprite {
- player: Player
}
export class PlayerSprite extends Phaser.GameObjects.Sprite implements AkkamonPlayerSprite {
- player: Player;
tilePos: Phaser.Math.Vector2;
constructor(config: PlayerSpriteConfig) {
@@ -30,10 +26,12 @@ export class PlayerSprite extends Phaser.GameObjects.Sprite implements AkkamonPl
config.texture,
config.frame);
- this.player = config.player;
this.tilePos = new Phaser.Math.Vector2(config.tilePos.x, config.tilePos.y);
this.setOrigin(0.5, 1);
+
+ // add to scene!
+ config.scene.add.existing(this);
}
getPosition(): Phaser.Math.Vector2 {
diff --git a/client/src/uiScene.ts b/client/src/uiScene.ts
new file mode 100644
index 0000000..8b92954
--- /dev/null
+++ b/client/src/uiScene.ts
@@ -0,0 +1,17 @@
+
+export class AkkamonUI extends Phaser.Scene
+{
+ constructor ()
+ {
+ super('AkkamonUI');
+ }
+
+ preload() {
+
+ }
+
+ create () {
+
+ }
+
+}