summaryrefslogtreecommitdiff
path: root/client
diff options
context:
space:
mode:
Diffstat (limited to 'client')
-rw-r--r--client/src/akkamon/DataWrappers.ts4
-rw-r--r--client/src/akkamon/GameConfig.ts2
-rw-r--r--client/src/akkamon/client/Client.ts58
-rw-r--r--client/src/akkamon/client/Events.ts48
-rw-r--r--client/src/akkamon/client/InteractionEngine.ts15
-rw-r--r--client/src/akkamon/client/Session.ts3
-rw-r--r--client/src/akkamon/client/Socket.ts14
-rw-r--r--client/src/akkamon/render/UIControls.ts2
-rw-r--r--client/src/akkamon/render/engine/AkkamonEngine.ts3
-rw-r--r--client/src/akkamon/scenes/AkkamonWorldScene.ts30
-rw-r--r--client/src/akkamon/scenes/UIElement.ts236
-rw-r--r--client/src/app.ts19
12 files changed, 403 insertions, 31 deletions
diff --git a/client/src/akkamon/DataWrappers.ts b/client/src/akkamon/DataWrappers.ts
index 22b25e2..6fff618 100644
--- a/client/src/akkamon/DataWrappers.ts
+++ b/client/src/akkamon/DataWrappers.ts
@@ -68,4 +68,8 @@ export class Stack<T> {
this._data = new Array();
}
+ cloneData() {
+ return new Array(this._data);
+ }
+
}
diff --git a/client/src/akkamon/GameConfig.ts b/client/src/akkamon/GameConfig.ts
index 8d7e4f1..53a10a2 100644
--- a/client/src/akkamon/GameConfig.ts
+++ b/client/src/akkamon/GameConfig.ts
@@ -10,7 +10,7 @@ export const gameConfig: Phaser.Types.Core.GameConfig & Phaser.Types.Core.Render
type: Phaser.AUTO,
backgroundColor: '#125555',
width: 800,
- height: 600,
+ height: 800,
pixelArt: true,
scene: [BootScene, DemoScene]
};
diff --git a/client/src/akkamon/client/Client.ts b/client/src/akkamon/client/Client.ts
index 40aa415..54a06f1 100644
--- a/client/src/akkamon/client/Client.ts
+++ b/client/src/akkamon/client/Client.ts
@@ -10,6 +10,8 @@ import { UIControls } from '../render/UIControls';
import { RemotePlayerEngine } from '../render/engine/RemotePlayerEngine';
+import { InteractionEngine } from './InteractionEngine';
+
import type { AkkamonClient } from './AkkamonClient';
import type { AkkamonWorldScene } from '../scenes/AkkamonWorldScene';
@@ -22,8 +24,15 @@ import {
HeartBeatReplyEvent,
IncomingEvent,
AkkamonEvent,
+ BattleRequestEvent,
} from './Events';
+function delay(ms: number) {
+ return new Promise(resolve => {
+ setTimeout(resolve, ms);
+ });
+}
+
export class Client implements AkkamonClient
{
@@ -36,15 +45,28 @@ export class Client implements AkkamonClient
private remotePlayerEngine?: RemotePlayerEngine;
+ private interactionEngine?: InteractionEngine
constructor(
- url: string
+ private url: string
) {
+
this.session = new Socket(url, this);
+
+ }
+
+ retryConnection() {
+ this.tryAgainLater(1000);
+ this.session = new Socket(this.url, this);
+ }
+
+ async tryAgainLater(ms: number) {
+ await delay(ms);
}
in(eventString: string) {
let event: IncomingEvent = JSON.parse(eventString);
+ // console.log(event);
switch (event.type) {
case EventType.HEART_BEAT:
if (this.remotePlayerEngine !== undefined) {
@@ -52,6 +74,16 @@ export class Client implements AkkamonClient
}
this.send(new HeartBeatReplyEvent());
break;
+ case EventType.TRAINER_REGISTRATION_REPLY:
+ if (event.trainerId !== undefined) {
+ console.log("setting Session trainerId to: " + event.trainerId);
+ this.session.trainerId = event.trainerId;
+ }
+ break;
+ case EventType.INIT_BATTLE_REPLY:
+ console.log("Received battle request reply!");
+ console.log(event);
+ break;
default:
console.log("ignored incoming event, doesn't match EventType interface.");
break;
@@ -70,6 +102,7 @@ export class Client implements AkkamonClient
this.controls!.update();
this.gridPhysics!.update(delta);
this.remotePlayerEngine!.update(delta);
+ this.interactionEngine!.update();
}
setUIControls(input: Phaser.Input.InputPlugin, menu: any) {
@@ -109,6 +142,11 @@ export class Client implements AkkamonClient
this.remotePlayerEngine = new RemotePlayerEngine(
scene
);
+
+ this.interactionEngine = new InteractionEngine(
+ scene
+ );
+
this.initAnimation(scene, playerSprite);
}
@@ -151,4 +189,22 @@ export class Client implements AkkamonClient
requestRemotePlayerData() {
return this.remotePlayerEngine!.getData();
}
+
+ sendBattleChallenge(remotePlayerName: string) {
+ console.log("sent a battle request!");
+ this.interactionEngine!.setAwaitingResponse();
+ this.send(new BattleRequestEvent(
+ this.getCurrentSceneKey(),
+ this.getSessionTrainerId(),
+ remotePlayerName
+ ));
+ }
+
+ getSessionTrainerId() {
+ return this.session.trainerId;
+ }
+
+ getCurrentSceneKey() {
+ return this.scene!.scene.key;
+ }
}
diff --git a/client/src/akkamon/client/Events.ts b/client/src/akkamon/client/Events.ts
index 830ac23..f816da8 100644
--- a/client/src/akkamon/client/Events.ts
+++ b/client/src/akkamon/client/Events.ts
@@ -3,10 +3,14 @@ import type { Direction } from '../render/Direction';
export enum EventType {
HEART_BEAT = "HeartBeat",
- PLAYER_REGISTRATION = "PlayerRegistrationEvent",
+ TRAINER_REGISTRATION_REQUEST = "TrainerRegistrationRequestEvent",
+ TRAINER_REGISTRATION_REPLY = "TrainerRegistrationReplyEvent",
START_MOVING = "StartMoving",
STOP_MOVING = "StopMoving",
- NEW_TILE_POS = "NewTilePos"
+ NEW_TILE_POS = "NewTilePos",
+ INIT_BATTLE_REQUEST = "InitBattleRequestEvent",
+ INIT_BATTLE_REPLY = "InitBattleReplyEvent",
+ BATTLE_ABORTED = "BattleAborted"
}
export interface AkkamonEvent {
@@ -19,18 +23,47 @@ export type RemoteMovementQueues = {
[trainerId: string]: { value: Array<Direction> }
}
+// INCOMING EVENTS
export interface IncomingEvent extends AkkamonEvent {
remoteMovementQueues?: RemoteMovementQueues
+ trainerId?: string
}
-export class PlayerRegistrationEvent implements AkkamonEvent {
+export class HeartBeatReplyEvent implements IncomingEvent {
- public type: EventType = EventType.PLAYER_REGISTRATION;
+ public type: EventType = EventType.HEART_BEAT;
+
+ constructor(
+ ) { }
+}
+
+export class PlayerRegistrationReplyEvent implements IncomingEvent {
+
+ public type: EventType = EventType.TRAINER_REGISTRATION_REPLY;
+
+ constructor(
+ public trainerId: string
+ ) { }
+}
+
+export class InitBattleReplyEvent implements IncomingEvent {
+
+ public type: EventType = EventType.INIT_BATTLE_REPLY;
constructor(
) { }
}
+// OUTGOING EVENTS
+export class PlayerRegistrationRequestEvent implements AkkamonEvent {
+
+ public type: EventType = EventType.TRAINER_REGISTRATION_REQUEST;
+
+ constructor(
+ ) { }
+}
+
+
export class StartMovingEvent implements AkkamonEvent {
public type: EventType = EventType.START_MOVING;
@@ -61,10 +94,13 @@ export class NewTilePosEvent implements AkkamonEvent {
) { }
}
-export class HeartBeatReplyEvent implements AkkamonEvent {
- public type: EventType = EventType.HEART_BEAT;
+export class InitBattleRequestEvent implements AkkamonEvent {
+
+ public type: EventType = EventType.INIT_BATTLE_REQUEST;
constructor(
+ public thisTrainer: string,
+ public otherTrainer: string
) { }
}
diff --git a/client/src/akkamon/client/InteractionEngine.ts b/client/src/akkamon/client/InteractionEngine.ts
new file mode 100644
index 0000000..5746b73
--- /dev/null
+++ b/client/src/akkamon/client/InteractionEngine.ts
@@ -0,0 +1,15 @@
+import { AkkamonEngine } from '../render/engine/AkkamonEngine';
+
+import type { AkkamonWorldScene } from '../scenes/AkkamonWorldScene';
+
+export class InteractionEngine extends AkkamonEngine {
+ constructor(scene: AkkamonWorldScene) {
+ super();
+ }
+
+ update() {
+ }
+
+ setAwaitingResponse() {
+ }
+}
diff --git a/client/src/akkamon/client/Session.ts b/client/src/akkamon/client/Session.ts
index bbefd66..6b6f5ec 100644
--- a/client/src/akkamon/client/Session.ts
+++ b/client/src/akkamon/client/Session.ts
@@ -1,7 +1,8 @@
import type Player from './player';
export default interface AkkamonSession extends WebSocket {
- user?: User
+ trainerId?: string
+
}
interface User {
diff --git a/client/src/akkamon/client/Socket.ts b/client/src/akkamon/client/Socket.ts
index e0c3e0c..b46c73f 100644
--- a/client/src/akkamon/client/Socket.ts
+++ b/client/src/akkamon/client/Socket.ts
@@ -2,11 +2,13 @@ import Phaser from 'phaser';
import type { Client } from './Client'
import type AkkamonSession from './Session'
import {
- PlayerRegistrationEvent
+ PlayerRegistrationRequestEvent
} from './Events';
export class Socket extends WebSocket implements AkkamonSession
{
+ public trainerId?: string;
+
constructor(
url: string,
client: Client
@@ -15,12 +17,20 @@ export class Socket extends WebSocket implements AkkamonSession
this.onopen = function echo(this: WebSocket, ev: Event) {
console.log("Sending PlayerRegistrationEvent.");
- client.send(new PlayerRegistrationEvent());
+ client.send(new PlayerRegistrationRequestEvent());
}
this.onmessage = function incomingMessage(this: WebSocket, ev: MessageEvent) {
client.in(ev.data);
}
+
+ // this.onerror = function socketFailure(this: WebSocket, ev: Event) {
+ // client.retryConnection();
+ // }
+
+ this.onclose = function socketClose(this: WebSocket, ev: Event) {
+ client.retryConnection();
+ }
}
}
diff --git a/client/src/akkamon/render/UIControls.ts b/client/src/akkamon/render/UIControls.ts
index 89eba32..0db6385 100644
--- a/client/src/akkamon/render/UIControls.ts
+++ b/client/src/akkamon/render/UIControls.ts
@@ -13,7 +13,7 @@ export class UIControls {
update() {
if (Phaser.Input.Keyboard.JustDown(this.cursors.left)) {
- this.menu.destroyMe();
+ this.menu.destroyAndGoBack();
} else if (Phaser.Input.Keyboard.JustDown(this.cursors.right)) {
this.menu.confirm();
} else if (Phaser.Input.Keyboard.JustDown(this.cursors.up)) {
diff --git a/client/src/akkamon/render/engine/AkkamonEngine.ts b/client/src/akkamon/render/engine/AkkamonEngine.ts
index 14dae86..02f7d58 100644
--- a/client/src/akkamon/render/engine/AkkamonEngine.ts
+++ b/client/src/akkamon/render/engine/AkkamonEngine.ts
@@ -4,4 +4,7 @@ import { client } from '../../../app';
export class AkkamonEngine {
client = client;
+ update(delta?: number) {
+ throw new Error("update must be implemented in Engines");
+ }
}
diff --git a/client/src/akkamon/scenes/AkkamonWorldScene.ts b/client/src/akkamon/scenes/AkkamonWorldScene.ts
index 6a97f4a..30af96c 100644
--- a/client/src/akkamon/scenes/AkkamonWorldScene.ts
+++ b/client/src/akkamon/scenes/AkkamonWorldScene.ts
@@ -55,12 +55,7 @@ export class AkkamonWorldScene extends Phaser.Scene {
let akey = this.input.keyboard.addKey('a');
akey.on('down', () => {
if (this.menus.isEmpty()) {
- this.menus.push(new PauseMenu(this));
- console.log("here is the menu stack:");
- console.log(this.menus);
- this.menuTakesUIControl(this.input, this.menus.peek());
- console.log("here is the menu stack, after taking controls:");
- console.log(this.menus);
+ this.pushMenu(new PauseMenu(this));
}
});
@@ -86,6 +81,8 @@ export class AkkamonWorldScene extends Phaser.Scene {
pushMenu(menu: AkkamonMenu) {
this.menus.push(menu);
this.menuTakesUIControl(this.input, menu);
+ console.log("New menu stack:");
+ console.log(this.menus);
}
popMenu() {
@@ -93,14 +90,14 @@ export class AkkamonWorldScene extends Phaser.Scene {
}
traverseMenusBackwards() {
- console.log("menu stack before traversing back:");
- console.log(this.menus);
this.popMenu();
if (!this.menus.isEmpty()) {
this.menuTakesUIControl(this.input, this.menus.peek());
} else {
this.isUsingGridControls();
}
+ console.log("menu stack after traversing back:");
+ console.log(this.menus);
}
getPlayerPixelPosition(): Phaser.Math.Vector2 {
@@ -115,4 +112,21 @@ export class AkkamonWorldScene extends Phaser.Scene {
return Array.from(remotePlayerData.keys());
}
}
+
+ requestBattleChallenge(remotePlayerName: string): void {
+ this.client.sendBattleChallenge(remotePlayerName);
+ }
+
+ clearMenus() {
+ if (this.menus.length > 0) {
+ this.menus.pop()!.destroyGroup();
+ console.log("stack while clearing menus:");
+ console.log(this.menus.cloneData());
+ this.clearMenus();
+ }
+ }
+
+ setWaitingOnResponse() {
+
+ }
}
diff --git a/client/src/akkamon/scenes/UIElement.ts b/client/src/akkamon/scenes/UIElement.ts
index 1f0de90..08905ef 100644
--- a/client/src/akkamon/scenes/UIElement.ts
+++ b/client/src/akkamon/scenes/UIElement.ts
@@ -1,12 +1,17 @@
import type { AkkamonWorldScene } from '../scenes/AkkamonWorldScene';
import { Direction } from '../render/Direction';
+import {
+ Queue
+} from '../DataWrappers';
class MenuText extends Phaser.GameObjects.Text {
+ public static TEXT_HEIGHT: number = 16;
+
constructor(scene: Phaser.Scene, group: Phaser.GameObjects.Group, groupDepth: number, x: number, y: number, text: string) {
let style: Phaser.Types.GameObjects.Text.TextStyle = {
fontFamily: 'Courier',
- fontSize: '16px',
+ fontSize: `${MenuText.TEXT_HEIGHT}px`,
fontStyle: '',
backgroundColor: undefined,
color: '#000000',
@@ -21,6 +26,30 @@ class MenuText extends Phaser.GameObjects.Text {
}
}
+class WrappedMenuText extends MenuText {
+ constructor(scene: Phaser.Scene, group: Phaser.GameObjects.Group, groupDepth: number, x: number, y: number, text: string, wrapWidth: number) {
+ super(scene, group, groupDepth, x, y, text);
+ this.setStyle({
+ fontFamily: 'Courier',
+ fontSize: '16px',
+ fontStyle: '',
+ backgroundColor: undefined,
+ color: '#000000',
+ stroke: '#000000',
+ strokeThickness: 0,
+ align: 'left', // 'left'|'center'|'right'|'justify'
+ wordWrap: {
+ width: wrapWidth,
+ useAdvancedWrap: true
+ }
+ });
+ }
+
+ destroy() {
+ super.destroy();
+ }
+}
+
class Picker extends Phaser.GameObjects.Image {
constructor(scene: Phaser.Scene, group: Phaser.GameObjects.Group, x: number, y: number, name: string) {
super(scene, x, y, name);
@@ -34,8 +63,9 @@ class Picker extends Phaser.GameObjects.Image {
export interface AkkamonMenu {
selectButton: (direction: Direction) => void
- destroyMe: () => void
+ destroyAndGoBack: () => void
confirm: () => void
+ destroyGroup: () => void
}
@@ -58,21 +88,26 @@ class Menu extends Phaser.GameObjects.Image implements AkkamonMenu {
ySpacing?: number
yOffsetFromTop?: number
xOffsetFromRight?: number
+ pickerOffset?:number
- destroyMe() {
- this.akkamonScene.traverseMenusBackwards();
+ destroyGroup() {
this.group!.destroy(true);
}
+ destroyAndGoBack() {
+ this.akkamonScene.traverseMenusBackwards();
+ this.destroyGroup();
+ }
+
confirm() {
// communicate with client
throw new Error('Confirm method should be present in a Menu implementation');
}
- constructor(scene: AkkamonWorldScene) {
+ constructor(scene: AkkamonWorldScene, imageKey: string) {
let camera = scene.cameras.main;
- super(scene, camera.scrollX + camera.width, camera.scrollY, "pause-menu")
+ super(scene, camera.scrollX, camera.scrollY, imageKey)
this.setOrigin(1,0)
this.setVisible(true)
this.setDisplaySize(296, 400)
@@ -143,8 +178,9 @@ class Menu extends Phaser.GameObjects.Image implements AkkamonMenu {
export class PauseMenu extends Menu implements AkkamonMenu {
constructor(scene: AkkamonWorldScene) {
- super(scene)
- this.ySpacing
+ super(scene, "pause-menu")
+ let camera = scene.cameras.main;
+ this.setPosition(this.x + camera.width, this.y);
this.setPicker(0);
this.setButtons([
'POKéDEX',
@@ -173,13 +209,25 @@ class ListMenu extends Menu implements AkkamonMenu {
scene: AkkamonWorldScene,
options: Array<string>
) {
- super(scene)
+ super(scene, "pause-menu")
+ let camera = scene.cameras.main;
+ this.setPosition(this.x + camera.width, this.y);
this.options = options;
+ if (this.viewBot > this.options.length) {
+ this.viewBot = this.options.length;
+ }
+
this.xOffsetFromRight = 210;
this.yOffsetFromTop = 50;
- let contacts = new MenuText(this.scene, this.group!, this.groupDepth!, this.x - this.xOffsetFromRight, this.y + 20, "Nearby trainers:")
+ let contacts = new MenuText(
+ this.scene,
+ this.group!,
+ this.groupDepth!,
+ this.x - this.xOffsetFromRight,
+ this.y + 20,
+ "Nearby trainers:")
// this.yOffsetFromTop
// this.ySpacing
@@ -232,4 +280,172 @@ class ListMenu extends Menu implements AkkamonMenu {
}
class RemotePlayerList extends ListMenu implements AkkamonMenu {
+
+ confirm() {
+ this.akkamonScene.pushMenu(new ChallengeDialogue(
+ this.akkamonScene,
+ ['YES', 'NO'],
+ {
+ 'trainerName': this.buttons![this.index! + this.viewTop].text
+ }));
+ }
+}
+
+class ConfirmationDialogue extends Menu implements AkkamonMenu {
+ dialogueData?: {[key: string]: string}
+ options?: Array<string>
+ dialogueBox?: Dialogue
+
+ constructor(scene: AkkamonWorldScene, options: Array<string>, dialogueData: {[key: string]: string}) {
+ super(scene, "confirmation-dialogue");
+ let camera = scene.cameras.main;
+ this.setDisplaySize(200, 0.83 * 200)
+ this.setPosition(this.x + camera.width, this.y + (camera.height - 0.28 * camera.width - this.displayHeight));
+ this.xOffsetFromRight = 0.5 * this.displayWidth;
+ this.yOffsetFromTop = 0.33 * this.displayHeight - MenuText.TEXT_HEIGHT;
+ this.ySpacing! = 0.33 * this.displayHeight;
+
+ this.setPicker(0);
+ this.setButtons(options);
+ this.groupDepth = 40;
+ this.group!.setDepth(this.groupDepth!);
+
+ this.dialogueBox = new Dialogue(scene, this.group!, this.groupDepth);
+ }
+}
+
+class Dialogue extends Phaser.GameObjects.Image implements AkkamonMenu {
+ public messageQueue: Queue<string>;
+ public displayedText: MenuText;
+ public akkamonScene: AkkamonWorldScene;
+ public group: Phaser.GameObjects.Group;
+
+ constructor(scene: AkkamonWorldScene, group: Phaser.GameObjects.Group, depth: number) {
+ let camera = scene.cameras.main;
+ super(scene, camera.scrollX, camera.scrollY, "general-dialogue-box")
+ this.setOrigin(0,1);
+ this.setPosition(camera.scrollX, camera.scrollY + camera.height)
+ this.setDisplaySize(camera.width, 0.28 * camera.width);
+
+ scene.add.existing(this);
+ group.add(this);
+ this.setDepth(depth);
+
+ this.group = group;
+ this.akkamonScene = scene;
+
+ this.messageQueue = new Queue();
+ this.displayedText = new WrappedMenuText(
+ this.akkamonScene,
+ this.group,
+ depth,
+ this.x + 40,
+ this.y - this.displayHeight + 40,
+ '',
+ camera.width
+ );
+ }
+
+ selectButton() {
+ }
+ confirm() {
+ }
+
+ destroyGroup() {
+ const clonedChildren = [... this.group.getChildren()]
+ for (let child of clonedChildren) {
+ console.log("destroying child with:");
+ console.log(child);
+ console.log(child.destroy);
+ child.destroy();
+ }
+ console.log("destroying group of dialogue");
+ console.log(this.group);
+ this.group.destroy(true)
+ }
+
+ destroyAndGoBack() {
+ console.log("Destroying dialogue box!");
+ this.akkamonScene.traverseMenusBackwards();
+ this.destroyGroup();
+ }
+
+ push(messageData: string | string[]): void {
+ if (typeof messageData === 'string') {
+ this.messageQueue.push(messageData);
+ } else if (Array.isArray(messageData)) {
+ this.messageQueue.pushArray(messageData);
+ }
+ }
+
+ displayNextDialogue() {
+ this.displayedText.text = '';
+ if (this.messageQueue.peek() !== undefined) {
+ this.typewriteText(this.messageQueue.pop()!);
+ }
+ }
+
+ typewriteText(text: string) {
+ const length = text.length
+ let i = 0
+ this.scene.time.addEvent({
+ callback: () => {
+ this.displayedText.text += text[i]
+ ++i
+ },
+ repeat: length - 1,
+ delay: 20
+ })
+ }
+
+}
+
+class ChallengeDialogue extends ConfirmationDialogue implements AkkamonMenu {
+ challengedTrainerName: string;
+ constructor(scene: AkkamonWorldScene, options: Array<string>, dialogueData: {[key: string]: string}) {
+ super(scene, options, dialogueData);
+ this.challengedTrainerName = dialogueData['trainerName'];
+ this.dialogueBox!.push(
+ `Do you want to challenge ${this.challengedTrainerName} to a battle?`
+ );
+ this.dialogueBox!.displayNextDialogue();
+ }
+
+ confirm() {
+ if (this.buttons![this.index!].text === "YES") {
+ this.akkamonScene.requestBattleChallenge(this.challengedTrainerName);
+ this.akkamonScene.clearMenus();
+ this.akkamonScene.pushMenu(new WaitingDialogue(this.akkamonScene, new Phaser.GameObjects.Group(this.scene), 20));
+ } else {
+ this.destroyAndGoBack();
+ }
+ }
+
+ destroy() {
+ this.scene.time.removeAllEvents();
+ super.destroy();
+ }
+}
+
+class WaitingDialogue extends Dialogue {
+ waitingPrinter: any
+ constructor(scene: AkkamonWorldScene, group: Phaser.GameObjects.Group, depth: number) {
+ super(scene, group, depth);
+ this.typewriteText("Waiting on reponse...");
+ this.waitingPrinter = setInterval(() => {
+ this.displayedText.text = '';
+ this.typewriteText("Waiting on reponse...");
+ }, 3000);
+ }
+
+ destroyAndGoBack() {
+ super.destroyAndGoBack();
+ }
+
+ destroy() {
+ console.log("destroying waiting dialogue!");
+ clearInterval(this.waitingPrinter);
+ this.scene.time.removeAllEvents();
+ super.destroy();
+ }
}
diff --git a/client/src/app.ts b/client/src/app.ts
index 197fb63..05fccb5 100644
--- a/client/src/app.ts
+++ b/client/src/app.ts
@@ -29,4 +29,21 @@ function destroyGame () {
export let client = new Client('ws://localhost:8080');
let game: Phaser.Game | null | undefined;
-if (!game) newGame();
+
+function delay(ms: number) {
+ return new Promise(resolve => {
+ setTimeout(resolve, ms);
+ });
+}
+
+async function awaitRegistrationReplyAndStart() {
+ if (!game) {
+ while (client.getSessionTrainerId() === undefined) {
+ console.log("can't start game, this trainerId is still undefined");
+ await delay(1000);
+ }
+ newGame();
+ }
+}
+
+awaitRegistrationReplyAndStart();