From 31a0d08c7681f67c48b46733ced48d2ba071ae9a Mon Sep 17 00:00:00 2001 From: TabascoEye Date: Sat, 30 Jan 2021 02:13:03 +0100 Subject: [PATCH 01/66] disable automatic gain control on microphone MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AGC really makes the soundqulity way worse than it could be. Especially since it is so noticeable when entering a Jitsi where AGC is disabled (and suddenly some people sound much better). In the long run, this should probably be configurable. On the other hand the setting changes the audio _going out_ from a user, so it might be hard for him to judge which settings are a good or a bad idea… --- front/src/WebRtc/MediaManager.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index 1a84d4a9..10a505cd 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -18,6 +18,12 @@ let videoConstraint: boolean|MediaTrackConstraints = { resizeMode: 'crop-and-scale', aspectRatio: 1.777777778 }; +let audioConstraint: boolean|MediaTrackConstraints = { + //TODO: make these values configurable in the game settings menu and store them in localstorage + autoGainControl: false, + echoCancellation: true, + noiseSuppression: false +}; export type UpdatedLocalStreamCallback = (media: MediaStream|null) => void; export type StartScreenSharingCallback = (media: MediaStream) => void; @@ -40,7 +46,7 @@ export class MediaManager { webrtcInAudio: HTMLAudioElement; private webrtcOutAudio: HTMLAudioElement; constraintsMedia : MediaStreamConstraints = { - audio: true, + audio: audioConstraint, video: videoConstraint }; updatedLocalStreamCallBacks : Set = new Set(); From 58b7d85bf3037f1b8de2b88e578da6dc37cfd3dc Mon Sep 17 00:00:00 2001 From: TabascoEye Date: Sat, 30 Jan 2021 02:24:36 +0100 Subject: [PATCH 02/66] bugfix and linting * use 'const' on constraints which never change * also re-enable constraints ater mic was re-enabled (after mute/unmute) --- front/src/WebRtc/MediaManager.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index 10a505cd..57879ca6 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -18,7 +18,7 @@ let videoConstraint: boolean|MediaTrackConstraints = { resizeMode: 'crop-and-scale', aspectRatio: 1.777777778 }; -let audioConstraint: boolean|MediaTrackConstraints = { +const audioConstraint: boolean|MediaTrackConstraints = { //TODO: make these values configurable in the game settings menu and store them in localstorage autoGainControl: false, echoCancellation: true, @@ -243,7 +243,7 @@ export class MediaManager { } public enableMicrophone() { - this.constraintsMedia.audio = true; + this.constraintsMedia.audio = audioConstraint; this.getCamera().then((stream) => { //TODO show error message tooltip upper of camera button From 80a5d2e30e29b340f25bd364caa24e7c3278e5e8 Mon Sep 17 00:00:00 2001 From: Johannes Berthel Date: Thu, 1 Apr 2021 00:33:05 +0200 Subject: [PATCH 03/66] added random companion to player --- front/src/Phaser/Companion/Companion.ts | 196 ++++++++++++++++++ .../src/Phaser/Companion/CompanionTextures.ts | 14 ++ .../CompanionTexturesLoadingManager.ts | 30 +++ front/src/Phaser/Player/Player.ts | 17 +- 4 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 front/src/Phaser/Companion/Companion.ts create mode 100644 front/src/Phaser/Companion/CompanionTextures.ts create mode 100644 front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts diff --git a/front/src/Phaser/Companion/Companion.ts b/front/src/Phaser/Companion/Companion.ts new file mode 100644 index 00000000..ab9b8be8 --- /dev/null +++ b/front/src/Phaser/Companion/Companion.ts @@ -0,0 +1,196 @@ +import Sprite = Phaser.GameObjects.Sprite; +import Container = Phaser.GameObjects.Container; +import { lazyLoadResource } from "./CompanionTexturesLoadingManager"; +import { PlayerAnimationDirections, PlayerAnimationTypes } from "../Player/Animation"; + +export class Companion extends Container { + public sprites: Map; + + private delta: number; + private invisible: boolean; + private target: { x: number, y: number }; + + constructor( + scene: Phaser.Scene, + x: number, + y: number + ) { + super(scene, x, y); + + this.delta = 0; + this.invisible = true; + this.target = { x, y }; + this.sprites = new Map(); + + const animal = ["dog1", "dog2", "dog3", "cat1", "cat2", "cat3"]; + const random = Math.floor(Math.random() * animal.length); + + lazyLoadResource(this.scene.load, animal[random]).then(resource => { + this.addResource(resource); + this.invisible = false; + }) + + this.scene.physics.world.enableBody(this); + + this.getBody().setImmovable(true); + this.getBody().setCollideWorldBounds(true); + this.setSize(16, 16); + this.getBody().setSize(16, 16); + this.getBody().setOffset(0, 8); + + this.setDepth(-1); + + scene.game.events.addListener('step', this.step.bind(this)); + scene.add.existing(this); + } + + public setTarget(x: number, y: number) { + this.target = { x, y }; + } + + private step(time: any, delta: any) { + if (typeof this.target === 'undefined') return; + + this.delta += delta; + if (this.delta < 256) { + return; + } + this.delta = 0; + + const xDist = this.target.x - this.x; + const yDist = this.target.y - this.y; + + let direction: PlayerAnimationDirections; + let type: PlayerAnimationTypes; + + const distance = Math.sqrt(Math.pow(Math.abs(xDist), 2) + Math.pow(Math.abs(yDist), 2)); + + if (distance < 16) { + type = PlayerAnimationTypes.Idle; + this.getBody().stop(); + } else { + type = PlayerAnimationTypes.Walk; + + const xDir = xDist / Math.max(Math.abs(xDist), 1); + const yDir = yDist / Math.max(Math.abs(yDist), 1); + + const speed = 256; + this.getBody().setVelocity(Math.min(Math.abs(xDist * 2), speed) * xDir, Math.min(Math.abs(yDist * 2), speed) * yDir); + } + + if (Math.abs(xDist) > Math.abs(yDist)) { + if (xDist < 0) { + direction = PlayerAnimationDirections.Left; + } else { + direction = PlayerAnimationDirections.Right; + } + } else { + if (yDist < 0) { + direction = PlayerAnimationDirections.Up; + } else { + direction = PlayerAnimationDirections.Down; + } + } + + this.setDepth(this.y); + this.playAnimation(direction, type); + } + + private playAnimation(direction: PlayerAnimationDirections, type: PlayerAnimationTypes): void { + if (this.invisible) return; + + for (const [resource, sprite] of this.sprites.entries()) { + sprite.play(`${resource}-${direction}-${type}`, true); + } + } + + private addResource(resource: string, frame?: string | number): void { + const sprite = new Sprite(this.scene, 0, 0, resource, frame); + + this.add(sprite); + + this.getAnimations(resource).forEach(animation => { + this.scene.anims.create(animation); + }); + + this.scene.sys.updateList.add(sprite); + this.sprites.set(resource, sprite); + } + + private getAnimations(resource: string): Phaser.Types.Animations.Animation[] { + return [ + { + key: `${resource}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Idle}`, + frames: this.scene.anims.generateFrameNumbers(resource, {frames: [1]}), + frameRate: 10, + repeat: 1 + }, + { + key: `${resource}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Idle}`, + frames: this.scene.anims.generateFrameNumbers(resource, {frames: [4]}), + frameRate: 10, + repeat: 1 + }, + { + key: `${resource}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Idle}`, + frames: this.scene.anims.generateFrameNumbers(resource, {frames: [7]}), + frameRate: 10, + repeat: 1 + }, + { + key: `${resource}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Idle}`, + frames: this.scene.anims.generateFrameNumbers(resource, {frames: [10]}), + frameRate: 10, + repeat: 1 + }, + { + key: `${resource}-${PlayerAnimationDirections.Down}-${PlayerAnimationTypes.Walk}`, + frames: this.scene.anims.generateFrameNumbers(resource, {frames: [0, 1, 2]}), + frameRate: 15, + repeat: -1 + }, + { + key: `${resource}-${PlayerAnimationDirections.Left}-${PlayerAnimationTypes.Walk}`, + frames: this.scene.anims.generateFrameNumbers(resource, {frames: [3, 4, 5]}), + frameRate: 15, + repeat: -1 + }, + { + key: `${resource}-${PlayerAnimationDirections.Right}-${PlayerAnimationTypes.Walk}`, + frames: this.scene.anims.generateFrameNumbers(resource, {frames: [6, 7, 8]}), + frameRate: 15, + repeat: -1 + }, + { + key: `${resource}-${PlayerAnimationDirections.Up}-${PlayerAnimationTypes.Walk}`, + frames: this.scene.anims.generateFrameNumbers(resource, {frames: [9, 10, 11]}), + frameRate: 15, + repeat: -1 + } + ] + } + + private getBody(): Phaser.Physics.Arcade.Body { + const body = this.body; + + if (!(body instanceof Phaser.Physics.Arcade.Body)) { + throw new Error('Container does not have arcade body'); + } + + return body; + } + + public destroy(): void { + for (const sprite of this.sprites.values()) { + if (this.scene) { + this.scene.sys.updateList.remove(sprite); + } + } + + if (this.scene) { + this.scene.game.events.removeListener('step', this.step.bind(this)); + } + + super.destroy(); + } +} diff --git a/front/src/Phaser/Companion/CompanionTextures.ts b/front/src/Phaser/Companion/CompanionTextures.ts new file mode 100644 index 00000000..84eaf38f --- /dev/null +++ b/front/src/Phaser/Companion/CompanionTextures.ts @@ -0,0 +1,14 @@ +export interface CompanionResourceDescriptionInterface { + name: string, + img: string, + behaviour: "dog" | "cat" +} + +export const COMPANION_RESOURCES: CompanionResourceDescriptionInterface[] = [ + { name: "dog1", img: "resources/characters/pipoya/Dog 01-1.png", behaviour: "dog" }, + { name: "dog2", img: "resources/characters/pipoya/Dog 01-2.png", behaviour: "dog" }, + { name: "dog3", img: "resources/characters/pipoya/Dog 01-3.png", behaviour: "dog" }, + { name: "cat1", img: "resources/characters/pipoya/Cat 01-1.png", behaviour: "cat" }, + { name: "cat2", img: "resources/characters/pipoya/Cat 01-2.png", behaviour: "cat" }, + { name: "cat3", img: "resources/characters/pipoya/Cat 01-3.png", behaviour: "cat" }, +] diff --git a/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts b/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts new file mode 100644 index 00000000..7023a34d --- /dev/null +++ b/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts @@ -0,0 +1,30 @@ +import LoaderPlugin = Phaser.Loader.LoaderPlugin; +import { TextureError } from "../../Exception/TextureError"; +import { COMPANION_RESOURCES, CompanionResourceDescriptionInterface } from "./CompanionTextures"; + +export const loadAll = (loader: LoaderPlugin): CompanionResourceDescriptionInterface[] => { + const resources = COMPANION_RESOURCES; + + resources.forEach((resource: CompanionResourceDescriptionInterface) => { + loader.spritesheet(resource.name, resource.img, { frameWidth: 32, frameHeight: 32, endFrame: 12 }); + }); + + return resources; +} + +export const lazyLoadResource = (loader: LoaderPlugin, name: string): Promise => { + const resource = COMPANION_RESOURCES.find(item => item.name === name); + + if (typeof resource === 'undefined') { + throw new TextureError(`Texture '${name}' not found!`); + } + + if (loader.textureManager.exists(resource.name)) { + return Promise.resolve(resource.name); + } + + return new Promise(resolve => { + loader.spritesheet(resource.name, resource.img, { frameWidth: 32, frameHeight: 32, endFrame: 12 }); + loader.once(`filecomplete-spritesheet-${resource.name}`, () => resolve(resource.name)); + }); +} diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index 64ba56d0..b7a2f7b4 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -2,7 +2,7 @@ import {PlayerAnimationDirections} from "./Animation"; import {GameScene} from "../Game/GameScene"; import {UserInputEvent, UserInputManager} from "../UserInput/UserInputManager"; import {Character} from "../Entity/Character"; - +import {Companion} from "../Companion/Companion"; export const hasMovedEventName = "hasMoved"; export interface CurrentGamerInterface extends Character{ @@ -13,6 +13,7 @@ export interface CurrentGamerInterface extends Character{ export class Player extends Character implements CurrentGamerInterface { private previousDirection: string = PlayerAnimationDirections.Down; private wasMoving: boolean = false; + private companion?: Companion; constructor( Scene: GameScene, @@ -28,6 +29,20 @@ export class Player extends Character implements CurrentGamerInterface { //the current player model should be push away by other players to prevent conflict this.getBody().setImmovable(false); + + this.addCompanion(); + } + + addCompanion(): void { + this.companion = new Companion(this.scene, this.x, this.y); + } + + move(x: number, y: number) { + super.move(x, y); + + if (this.companion) { + this.companion.setTarget(this.x, this.y); + } } moveUser(delta: number): void { From 5a91e1558088ecd2c2e1c8914e5c48f8d6e49c59 Mon Sep 17 00:00:00 2001 From: Johannes Berthel Date: Thu, 1 Apr 2021 18:51:51 +0200 Subject: [PATCH 04/66] add companion to remote player --- front/src/Phaser/Companion/Companion.ts | 47 ++++++++++++++----------- front/src/Phaser/Entity/Character.ts | 16 +++++++++ front/src/Phaser/Entity/RemotePlayer.ts | 4 +++ front/src/Phaser/Player/Player.ts | 16 --------- 4 files changed, 46 insertions(+), 37 deletions(-) diff --git a/front/src/Phaser/Companion/Companion.ts b/front/src/Phaser/Companion/Companion.ts index ab9b8be8..065b3a8d 100644 --- a/front/src/Phaser/Companion/Companion.ts +++ b/front/src/Phaser/Companion/Companion.ts @@ -8,18 +8,19 @@ export class Companion extends Container { private delta: number; private invisible: boolean; - private target: { x: number, y: number }; + private stepListener: Function; + private target: { x: number, y: number, direction: PlayerAnimationDirections }; constructor( scene: Phaser.Scene, x: number, y: number ) { - super(scene, x, y); + super(scene, x + 8, y + 8); this.delta = 0; this.invisible = true; - this.target = { x, y }; + this.target = { x, y, direction: PlayerAnimationDirections.Down }; this.sprites = new Map(); const animal = ["dog1", "dog2", "dog3", "cat1", "cat2", "cat3"]; @@ -40,19 +41,21 @@ export class Companion extends Container { this.setDepth(-1); - scene.game.events.addListener('step', this.step.bind(this)); + this.stepListener = this.step.bind(this); + + scene.game.events.addListener('step', this.stepListener); scene.add.existing(this); } - public setTarget(x: number, y: number) { - this.target = { x, y }; + public setTarget(x: number, y: number, direction: PlayerAnimationDirections) { + this.target = { x, y, direction }; } private step(time: any, delta: any) { if (typeof this.target === 'undefined') return; this.delta += delta; - if (this.delta < 256) { + if (this.delta < 128) { return; } this.delta = 0; @@ -63,10 +66,12 @@ export class Companion extends Container { let direction: PlayerAnimationDirections; let type: PlayerAnimationTypes; - const distance = Math.sqrt(Math.pow(Math.abs(xDist), 2) + Math.pow(Math.abs(yDist), 2)); + const distance = Math.sqrt(Math.pow(xDist, 2) + Math.pow(yDist, 2)); if (distance < 16) { type = PlayerAnimationTypes.Idle; + direction = this.target.direction; + this.getBody().stop(); } else { type = PlayerAnimationTypes.Walk; @@ -76,22 +81,22 @@ export class Companion extends Container { const speed = 256; this.getBody().setVelocity(Math.min(Math.abs(xDist * 2), speed) * xDir, Math.min(Math.abs(yDist * 2), speed) * yDir); - } - if (Math.abs(xDist) > Math.abs(yDist)) { - if (xDist < 0) { - direction = PlayerAnimationDirections.Left; + if (Math.abs(xDist) > Math.abs(yDist)) { + if (xDist < 0) { + direction = PlayerAnimationDirections.Left; + } else { + direction = PlayerAnimationDirections.Right; + } } else { - direction = PlayerAnimationDirections.Right; - } - } else { - if (yDist < 0) { - direction = PlayerAnimationDirections.Up; - } else { - direction = PlayerAnimationDirections.Down; + if (yDist < 0) { + direction = PlayerAnimationDirections.Up; + } else { + direction = PlayerAnimationDirections.Down; + } } } - + this.setDepth(this.y); this.playAnimation(direction, type); } @@ -188,7 +193,7 @@ export class Companion extends Container { } if (this.scene) { - this.scene.game.events.removeListener('step', this.step.bind(this)); + this.scene.game.events.removeListener('step', this.stepListener); } super.destroy(); diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index 797616f8..352494f8 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -4,6 +4,7 @@ import BitmapText = Phaser.GameObjects.BitmapText; import Container = Phaser.GameObjects.Container; import Sprite = Phaser.GameObjects.Sprite; import {TextureError} from "../../Exception/TextureError"; +import {Companion} from "../Companion/Companion"; interface AnimationData { key: string; @@ -21,6 +22,7 @@ export abstract class Character extends Container { private lastDirection: PlayerAnimationDirections = PlayerAnimationDirections.Down; //private teleportation: Sprite; private invisible: boolean; + public companion?: Companion; constructor(scene: Phaser.Scene, x: number, @@ -67,6 +69,12 @@ export abstract class Character extends Container { this.setDepth(-1); this.playAnimation(direction, moving); + + this.addCompanion(); + } + + private addCompanion(): void { + this.companion = new Companion(this.scene, this.x, this.y); } public addTextures(textures: string[], frame?: string | number): void { @@ -189,6 +197,10 @@ export abstract class Character extends Container { } this.setDepth(this.y); + + if (this.companion) { + this.companion.setTarget(this.x, this.y, this.lastDirection); + } } stop(){ @@ -215,5 +227,9 @@ export abstract class Character extends Container { } super.destroy(); this.playerName.destroy(); + + if (this.companion) { + this.companion.destroy(); + } } } diff --git a/front/src/Phaser/Entity/RemotePlayer.ts b/front/src/Phaser/Entity/RemotePlayer.ts index a6bd4f40..4fe18fc6 100644 --- a/front/src/Phaser/Entity/RemotePlayer.ts +++ b/front/src/Phaser/Entity/RemotePlayer.ts @@ -31,5 +31,9 @@ export class RemotePlayer extends Character { this.setY(position.y); this.setDepth(position.y); //this is to make sure the perspective (player models closer the bottom of the screen will appear in front of models nearer the top of the screen). + + if (this.companion) { + this.companion.setTarget(position.x, position.y, position.direction as PlayerAnimationDirections); + } } } diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index b7a2f7b4..ac8942a5 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -2,7 +2,6 @@ import {PlayerAnimationDirections} from "./Animation"; import {GameScene} from "../Game/GameScene"; import {UserInputEvent, UserInputManager} from "../UserInput/UserInputManager"; import {Character} from "../Entity/Character"; -import {Companion} from "../Companion/Companion"; export const hasMovedEventName = "hasMoved"; export interface CurrentGamerInterface extends Character{ @@ -13,7 +12,6 @@ export interface CurrentGamerInterface extends Character{ export class Player extends Character implements CurrentGamerInterface { private previousDirection: string = PlayerAnimationDirections.Down; private wasMoving: boolean = false; - private companion?: Companion; constructor( Scene: GameScene, @@ -29,20 +27,6 @@ export class Player extends Character implements CurrentGamerInterface { //the current player model should be push away by other players to prevent conflict this.getBody().setImmovable(false); - - this.addCompanion(); - } - - addCompanion(): void { - this.companion = new Companion(this.scene, this.x, this.y); - } - - move(x: number, y: number) { - super.move(x, y); - - if (this.companion) { - this.companion.setTarget(this.x, this.y); - } } moveUser(delta: number): void { From 2ad712807ba13e9f32d974a284ba2961fb3b15ae Mon Sep 17 00:00:00 2001 From: Johannes Berthel Date: Fri, 2 Apr 2021 17:14:34 +0200 Subject: [PATCH 05/66] add companion only on local player --- front/src/Phaser/Companion/Companion.ts | 70 ++++++++++++++++--------- front/src/Phaser/Entity/Character.ts | 4 +- front/src/Phaser/Player/Player.ts | 7 +++ 3 files changed, 53 insertions(+), 28 deletions(-) diff --git a/front/src/Phaser/Companion/Companion.ts b/front/src/Phaser/Companion/Companion.ts index 065b3a8d..60e58995 100644 --- a/front/src/Phaser/Companion/Companion.ts +++ b/front/src/Phaser/Companion/Companion.ts @@ -3,14 +3,25 @@ import Container = Phaser.GameObjects.Container; import { lazyLoadResource } from "./CompanionTexturesLoadingManager"; import { PlayerAnimationDirections, PlayerAnimationTypes } from "../Player/Animation"; +export interface CompanionStatus { + x: number; + y: number; + name: string; + moving: boolean; + direction: PlayerAnimationDirections; +} + export class Companion extends Container { public sprites: Map; private delta: number; private invisible: boolean; - private stepListener: Function; private target: { x: number, y: number, direction: PlayerAnimationDirections }; + private companionName: string; + private direction: PlayerAnimationDirections; + private animationType: PlayerAnimationTypes; + constructor( scene: Phaser.Scene, x: number, @@ -18,15 +29,22 @@ export class Companion extends Container { ) { super(scene, x + 8, y + 8); + this.sprites = new Map(); + this.delta = 0; this.invisible = true; this.target = { x, y, direction: PlayerAnimationDirections.Down }; - this.sprites = new Map(); + this.direction = PlayerAnimationDirections.Down; + this.animationType = PlayerAnimationTypes.Idle; + + // select random animal const animal = ["dog1", "dog2", "dog3", "cat1", "cat2", "cat3"]; const random = Math.floor(Math.random() * animal.length); - lazyLoadResource(this.scene.load, animal[random]).then(resource => { + this.companionName = animal[random]; + + lazyLoadResource(this.scene.load, this.companionName).then(resource => { this.addResource(resource); this.invisible = false; }) @@ -34,16 +52,13 @@ export class Companion extends Container { this.scene.physics.world.enableBody(this); this.getBody().setImmovable(true); - this.getBody().setCollideWorldBounds(true); + this.getBody().setCollideWorldBounds(false); this.setSize(16, 16); this.getBody().setSize(16, 16); this.getBody().setOffset(0, 8); this.setDepth(-1); - this.stepListener = this.step.bind(this); - - scene.game.events.addListener('step', this.stepListener); scene.add.existing(this); } @@ -51,7 +66,7 @@ export class Companion extends Container { this.target = { x, y, direction }; } - private step(time: any, delta: any) { + public step(delta: number) { if (typeof this.target === 'undefined') return; this.delta += delta; @@ -63,18 +78,15 @@ export class Companion extends Container { const xDist = this.target.x - this.x; const yDist = this.target.y - this.y; - let direction: PlayerAnimationDirections; - let type: PlayerAnimationTypes; + const distance = Math.pow(xDist, 2) + Math.pow(yDist, 2); - const distance = Math.sqrt(Math.pow(xDist, 2) + Math.pow(yDist, 2)); - - if (distance < 16) { - type = PlayerAnimationTypes.Idle; - direction = this.target.direction; + if (distance < 576) { // 24^2 + this.animationType = PlayerAnimationTypes.Idle; + this.direction = this.target.direction; this.getBody().stop(); } else { - type = PlayerAnimationTypes.Walk; + this.animationType = PlayerAnimationTypes.Walk; const xDir = xDist / Math.max(Math.abs(xDist), 1); const yDir = yDist / Math.max(Math.abs(yDist), 1); @@ -84,21 +96,33 @@ export class Companion extends Container { if (Math.abs(xDist) > Math.abs(yDist)) { if (xDist < 0) { - direction = PlayerAnimationDirections.Left; + this.direction = PlayerAnimationDirections.Left; } else { - direction = PlayerAnimationDirections.Right; + this.direction = PlayerAnimationDirections.Right; } } else { if (yDist < 0) { - direction = PlayerAnimationDirections.Up; + this.direction = PlayerAnimationDirections.Up; } else { - direction = PlayerAnimationDirections.Down; + this.direction = PlayerAnimationDirections.Down; } } } this.setDepth(this.y); - this.playAnimation(direction, type); + this.playAnimation(this.direction, this.animationType); + } + + public getStatus(): CompanionStatus { + const { x, y, direction, animationType, companionName } = this; + + return { + x, + y, + direction, + moving: animationType === PlayerAnimationTypes.Walk, + name: companionName + } } private playAnimation(direction: PlayerAnimationDirections, type: PlayerAnimationTypes): void { @@ -192,10 +216,6 @@ export class Companion extends Container { } } - if (this.scene) { - this.scene.game.events.removeListener('step', this.stepListener); - } - super.destroy(); } } diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index 352494f8..f329192c 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -69,11 +69,9 @@ export abstract class Character extends Container { this.setDepth(-1); this.playAnimation(direction, moving); - - this.addCompanion(); } - private addCompanion(): void { + public addCompanion(): void { this.companion = new Companion(this.scene, this.x, this.y); } diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index ac8942a5..af63ddb3 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -27,6 +27,8 @@ export class Player extends Character implements CurrentGamerInterface { //the current player model should be push away by other players to prevent conflict this.getBody().setImmovable(false); + + this.addCompanion(); } moveUser(delta: number): void { @@ -58,6 +60,11 @@ export class Player extends Character implements CurrentGamerInterface { direction = PlayerAnimationDirections.Right; moving = true; } + + if (this.companion) { + this.companion.step(delta); + } + if (x !== 0 || y !== 0) { this.move(x, y); this.emit(hasMovedEventName, {moving, direction, x: this.x, y: this.y}); From c07079051a39d0cddf40e1d4846d8490840630a2 Mon Sep 17 00:00:00 2001 From: Johannes Berthel Date: Fri, 2 Apr 2021 21:21:11 +0200 Subject: [PATCH 06/66] transmit companion to remote players --- back/src/Model/GameRoom.ts | 3 ++- back/src/Model/User.ts | 5 +++-- back/src/Services/SocketManager.ts | 2 ++ front/src/Connexion/ConnectionManager.ts | 6 +++--- front/src/Connexion/ConnexionModels.ts | 4 +++- front/src/Connexion/LocalUserStore.ts | 8 ++++++++ front/src/Connexion/RoomConnection.ts | 11 ++++++++-- front/src/Phaser/Companion/Companion.ts | 20 ++++++++----------- front/src/Phaser/Entity/Character.ts | 4 ++-- front/src/Phaser/Entity/RemotePlayer.ts | 9 +++++++-- front/src/Phaser/Game/AddPlayerInterface.ts | 1 + front/src/Phaser/Game/GameManager.ts | 5 +++++ front/src/Phaser/Game/GameScene.ts | 16 ++++++++++----- front/src/Phaser/Player/Player.ts | 11 +++++----- messages/protos/messages.proto | 7 +++++++ pusher/src/Controller/IoSocketController.ts | 12 ++++++++++- .../src/Model/Websocket/ExSocketInterface.ts | 2 ++ pusher/src/Model/Zone.ts | 8 +++++--- pusher/src/Services/SocketManager.ts | 2 ++ 19 files changed, 96 insertions(+), 40 deletions(-) diff --git a/back/src/Model/GameRoom.ts b/back/src/Model/GameRoom.ts index 6a592ed0..430dbc48 100644 --- a/back/src/Model/GameRoom.ts +++ b/back/src/Model/GameRoom.ts @@ -110,7 +110,8 @@ export class GameRoom { socket, joinRoomMessage.getTagList(), joinRoomMessage.getName(), - ProtobufUtils.toCharacterLayerObjects(joinRoomMessage.getCharacterlayerList()) + ProtobufUtils.toCharacterLayerObjects(joinRoomMessage.getCharacterlayerList()), + joinRoomMessage.getCompanion() ); this.nextUserId++; this.users.set(user.id, user); diff --git a/back/src/Model/User.ts b/back/src/Model/User.ts index 51a1a617..370781e5 100644 --- a/back/src/Model/User.ts +++ b/back/src/Model/User.ts @@ -4,7 +4,7 @@ import {Zone} from "_Model/Zone"; import {Movable} from "_Model/Movable"; import {PositionNotifier} from "_Model/PositionNotifier"; import {ServerDuplexStream} from "grpc"; -import {BatchMessage, PusherToBackMessage, ServerToClientMessage, SubMessage} from "../Messages/generated/messages_pb"; +import {BatchMessage, Companion, PusherToBackMessage, ServerToClientMessage, SubMessage} from "../Messages/generated/messages_pb"; import {CharacterLayer} from "_Model/Websocket/CharacterLayer"; export type UserSocket = ServerDuplexStream; @@ -23,7 +23,8 @@ export class User implements Movable { public readonly socket: UserSocket, public readonly tags: string[], public readonly name: string, - public readonly characterLayers: CharacterLayer[] + public readonly characterLayers: CharacterLayer[], + public readonly companion?: Companion ) { this.listenedZones = new Set(); diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index c03f4773..166622f9 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -325,6 +325,7 @@ export class SocketManager { userJoinedZoneMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(thing.characterLayers)); userJoinedZoneMessage.setPosition(ProtobufUtils.toPositionMessage(thing.getPosition())); userJoinedZoneMessage.setFromzone(this.toProtoZone(fromZone)); + userJoinedZoneMessage.setCompanion(thing.companion); const subMessage = new SubToPusherMessage(); subMessage.setUserjoinedzonemessage(userJoinedZoneMessage); @@ -634,6 +635,7 @@ export class SocketManager { userJoinedMessage.setName(thing.name); userJoinedMessage.setCharacterlayersList(ProtobufUtils.toCharacterLayerMessages(thing.characterLayers)); userJoinedMessage.setPosition(ProtobufUtils.toPositionMessage(thing.getPosition())); + userJoinedMessage.setCompanion(thing.companion); const subMessage = new SubToPusherMessage(); subMessage.setUserjoinedzonemessage(userJoinedMessage); diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts index ed920aa0..e1e0db0a 100644 --- a/front/src/Connexion/ConnectionManager.ts +++ b/front/src/Connexion/ConnectionManager.ts @@ -88,9 +88,9 @@ class ConnectionManager { this.localUser = new LocalUser('', 'test', []); } - public connectToRoomSocket(roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface): Promise { + public connectToRoomSocket(roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface, companion: string|null): Promise { return new Promise((resolve, reject) => { - const connection = new RoomConnection(this.localUser.jwtToken, roomId, name, characterLayers, position, viewport); + const connection = new RoomConnection(this.localUser.jwtToken, roomId, name, characterLayers, position, viewport, companion); connection.onConnectError((error: object) => { console.log('An error occurred while connecting to socket server. Retrying'); reject(error); @@ -111,7 +111,7 @@ class ConnectionManager { this.reconnectingTimeout = setTimeout(() => { //todo: allow a way to break recursion? //todo: find a way to avoid recursive function. Otherwise, the call stack will grow indefinitely. - this.connectToRoomSocket(roomId, name, characterLayers, position, viewport).then((connection) => resolve(connection)); + this.connectToRoomSocket(roomId, name, characterLayers, position, viewport, companion).then((connection) => resolve(connection)); }, 4000 + Math.floor(Math.random() * 2000) ); }); }); diff --git a/front/src/Connexion/ConnexionModels.ts b/front/src/Connexion/ConnexionModels.ts index 519afcd3..477e86e3 100644 --- a/front/src/Connexion/ConnexionModels.ts +++ b/front/src/Connexion/ConnexionModels.ts @@ -47,6 +47,7 @@ export interface MessageUserPositionInterface { name: string; characterLayers: BodyResourceDescriptionInterface[]; position: PointInterface; + companion: string|null; } export interface MessageUserMovedInterface { @@ -58,7 +59,8 @@ export interface MessageUserJoined { userId: number; name: string; characterLayers: BodyResourceDescriptionInterface[]; - position: PointInterface + position: PointInterface; + companion: string|null; } export interface PositionInterface { diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts index 702df561..4989a9ea 100644 --- a/front/src/Connexion/LocalUserStore.ts +++ b/front/src/Connexion/LocalUserStore.ts @@ -4,6 +4,7 @@ const playerNameKey = 'playerName'; const selectedPlayerKey = 'selectedPlayer'; const customCursorPositionKey = 'customCursorPosition'; const characterLayersKey = 'characterLayers'; +const companionKey = 'companion'; const gameQualityKey = 'gameQuality'; const videoQualityKey = 'videoQuality'; const audioPlayerVolumeKey = 'audioVolume'; @@ -47,6 +48,13 @@ class LocalUserStore { return JSON.parse(localStorage.getItem(characterLayersKey) || "null"); } + setCompanion(companion: string): void { + localStorage.setItem(companionKey, companion); + } + getCompanion(): string|null { + return localStorage.getItem(companionKey); + } + setGameQualityValue(value: number): void { localStorage.setItem(gameQualityKey, '' + value); } diff --git a/front/src/Connexion/RoomConnection.ts b/front/src/Connexion/RoomConnection.ts index 584dae06..391d227d 100644 --- a/front/src/Connexion/RoomConnection.ts +++ b/front/src/Connexion/RoomConnection.ts @@ -66,7 +66,7 @@ export class RoomConnection implements RoomConnection { * @param token A JWT token containing the UUID of the user * @param roomId The ID of the room in the form "_/[instance]/[map_url]" or "@/[org]/[event]/[map]" */ - public constructor(token: string|null, roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface) { + public constructor(token: string|null, roomId: string, name: string, characterLayers: string[], position: PositionInterface, viewport: ViewportInterface, companion: string|null) { let url = API_URL.replace('http://', 'ws://').replace('https://', 'wss://'); url += '/room'; url += '?roomId='+(roomId ?encodeURIComponent(roomId):''); @@ -81,6 +81,10 @@ export class RoomConnection implements RoomConnection { url += '&bottom='+Math.floor(viewport.bottom); url += '&left='+Math.floor(viewport.left); url += '&right='+Math.floor(viewport.right); + + if (typeof companion === 'string') { + url += '&companion='+encodeURIComponent(companion); + } if (RoomConnection.websocketFactory) { this.socket = RoomConnection.websocketFactory(url); @@ -316,11 +320,14 @@ export class RoomConnection implements RoomConnection { } }) + const companion = message.getCompanion(); + return { userId: message.getUserid(), name: message.getName(), characterLayers, - position: ProtobufClientUtils.toPointInterface(position) + position: ProtobufClientUtils.toPointInterface(position), + companion: companion ? companion.getName() : null } } diff --git a/front/src/Phaser/Companion/Companion.ts b/front/src/Phaser/Companion/Companion.ts index 60e58995..196da09e 100644 --- a/front/src/Phaser/Companion/Companion.ts +++ b/front/src/Phaser/Companion/Companion.ts @@ -22,11 +22,7 @@ export class Companion extends Container { private direction: PlayerAnimationDirections; private animationType: PlayerAnimationTypes; - constructor( - scene: Phaser.Scene, - x: number, - y: number - ) { + constructor(scene: Phaser.Scene, x: number, y: number, name: string) { super(scene, x + 8, y + 8); this.sprites = new Map(); @@ -38,11 +34,7 @@ export class Companion extends Container { this.direction = PlayerAnimationDirections.Down; this.animationType = PlayerAnimationTypes.Idle; - // select random animal - const animal = ["dog1", "dog2", "dog3", "cat1", "cat2", "cat3"]; - const random = Math.floor(Math.random() * animal.length); - - this.companionName = animal[random]; + this.companionName = name; lazyLoadResource(this.scene.load, this.companionName).then(resource => { this.addResource(resource); @@ -59,14 +51,16 @@ export class Companion extends Container { this.setDepth(-1); - scene.add.existing(this); + this.scene.events.addListener('update', this.step, this); + + this.scene.add.existing(this); } public setTarget(x: number, y: number, direction: PlayerAnimationDirections) { this.target = { x, y, direction }; } - public step(delta: number) { + public step(time: number, delta: number) { if (typeof this.target === 'undefined') return; this.delta += delta; @@ -216,6 +210,8 @@ export class Companion extends Container { } } + this.scene.events.removeListener('update', this.step, this); + super.destroy(); } } diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index f329192c..eb8e45fd 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -71,8 +71,8 @@ export abstract class Character extends Container { this.playAnimation(direction, moving); } - public addCompanion(): void { - this.companion = new Companion(this.scene, this.x, this.y); + public addCompanion(name: string): void { + this.companion = new Companion(this.scene, this.x, this.y, name); } public addTextures(textures: string[], frame?: string | number): void { diff --git a/front/src/Phaser/Entity/RemotePlayer.ts b/front/src/Phaser/Entity/RemotePlayer.ts index 4fe18fc6..b405d8df 100644 --- a/front/src/Phaser/Entity/RemotePlayer.ts +++ b/front/src/Phaser/Entity/RemotePlayer.ts @@ -17,12 +17,17 @@ export class RemotePlayer extends Character { name: string, texturesPromise: Promise, direction: PlayerAnimationDirections, - moving: boolean + moving: boolean, + companion: string|null ) { super(Scene, x, y, texturesPromise, name, direction, moving, 1); - + //set data this.userId = userId; + + if (typeof companion === 'string') { + this.addCompanion(companion); + } } updatePosition(position: PointInterface): void { diff --git a/front/src/Phaser/Game/AddPlayerInterface.ts b/front/src/Phaser/Game/AddPlayerInterface.ts index 0edf197e..96762a66 100644 --- a/front/src/Phaser/Game/AddPlayerInterface.ts +++ b/front/src/Phaser/Game/AddPlayerInterface.ts @@ -6,4 +6,5 @@ export interface AddPlayerInterface { name: string; characterLayers: BodyResourceDescriptionInterface[]; position: PointInterface; + companion: string|null; } diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts index 8751796f..fbe5102c 100644 --- a/front/src/Phaser/Game/GameManager.ts +++ b/front/src/Phaser/Game/GameManager.ts @@ -21,12 +21,14 @@ export interface HasMovedEvent { export class GameManager { private playerName: string|null; private characterLayers: string[]|null; + private companion: string|null; private startRoom!:Room; currentGameSceneName: string|null = null; constructor() { this.playerName = localUserStore.getName(); this.characterLayers = localUserStore.getCharacterLayers(); + this.companion = localUserStore.getCompanion(); } public async init(scenePlugin: Phaser.Scenes.ScenePlugin): Promise { @@ -63,6 +65,9 @@ export class GameManager { return this.characterLayers; } + getCompanion(): string|null { + return this.companion; + } public async loadMap(room: Room, scenePlugin: Phaser.Scenes.ScenePlugin): Promise { const roomID = room.id; diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 51388169..222f7ed5 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -159,6 +159,7 @@ export class GameScene extends ResizableScene implements CenterListener { private openChatIcon!: OpenChatIcon; private playerName!: string; private characterLayers!: string[]; + private companion!: string|null; private messageSubscription: Subscription|null = null; private popUpElements : Map = new Map(); @@ -335,7 +336,7 @@ export class GameScene extends ResizableScene implements CenterListener { } this.playerName = playerName; this.characterLayers = gameManager.getCharacterLayers(); - + this.companion = gameManager.getCompanion(); //initalise map this.Map = this.add.tilemap(this.MapUrlFile); @@ -459,7 +460,9 @@ export class GameScene extends ResizableScene implements CenterListener { top: camera.scrollY, right: camera.scrollX + camera.width, bottom: camera.scrollY + camera.height, - }).then((onConnect: OnConnectInterface) => { + }, + this.companion + ).then((onConnect: OnConnectInterface) => { this.connection = onConnect.connection; this.connection.onUserJoins((message: MessageUserJoined) => { @@ -467,7 +470,8 @@ export class GameScene extends ResizableScene implements CenterListener { userId: message.userId, characterLayers: message.characterLayers, name: message.name, - position: message.position + position: message.position, + companion: message.companion } this.addPlayer(userMessage); }); @@ -1019,7 +1023,8 @@ ${escapedMessage} texturesPromise, PlayerAnimationDirections.Down, false, - this.userInputManager + this.userInputManager, + this.companion ); }catch (err){ if(err instanceof TextureError) { @@ -1211,7 +1216,8 @@ ${escapedMessage} addPlayerData.name, texturesPromise, addPlayerData.position.direction as PlayerAnimationDirections, - addPlayerData.position.moving + addPlayerData.position.moving, + addPlayerData.companion ); this.MapPlayers.add(player); this.MapPlayersByKey.set(player.userId, player); diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index af63ddb3..d018a41f 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -21,14 +21,17 @@ export class Player extends Character implements CurrentGamerInterface { texturesPromise: Promise, direction: PlayerAnimationDirections, moving: boolean, - private userInputManager: UserInputManager + private userInputManager: UserInputManager, + companion: string|null ) { super(Scene, x, y, texturesPromise, name, direction, moving, 1); //the current player model should be push away by other players to prevent conflict this.getBody().setImmovable(false); - this.addCompanion(); + if (typeof companion === 'string') { + this.addCompanion(companion); + } } moveUser(delta: number): void { @@ -61,10 +64,6 @@ export class Player extends Character implements CurrentGamerInterface { moving = true; } - if (this.companion) { - this.companion.step(delta); - } - if (x !== 0 || y !== 0) { this.move(x, y); this.emit(hasMovedEventName, {moving, direction, x: this.x, y: this.y}); diff --git a/messages/protos/messages.proto b/messages/protos/messages.proto index cc23ed24..eee3be86 100644 --- a/messages/protos/messages.proto +++ b/messages/protos/messages.proto @@ -36,6 +36,10 @@ message CharacterLayerMessage { string name = 2; } +message Companion { + string name = 1; +} + /*********** CLIENT TO SERVER MESSAGES *************/ message PingMessage { @@ -141,6 +145,7 @@ message UserJoinedMessage { string name = 2; repeated CharacterLayerMessage characterLayers = 3; PositionMessage position = 4; + Companion companion = 5; } message UserLeftMessage { @@ -243,6 +248,7 @@ message JoinRoomMessage { string roomId = 5; repeated string tag = 6; string IPAddress = 7; + Companion companion = 8; } message UserJoinedZoneMessage { @@ -251,6 +257,7 @@ message UserJoinedZoneMessage { repeated CharacterLayerMessage characterLayers = 3; PositionMessage position = 4; Zone fromZone = 5; + Companion companion = 6; } message UserLeftZoneMessage { diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index 85a80e11..55a8e032 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -12,7 +12,7 @@ import { WebRtcSignalToServerMessage, PlayGlobalMessage, ReportPlayerMessage, - QueryJitsiJwtMessage, SendUserMessage, ServerToClientMessage + QueryJitsiJwtMessage, SendUserMessage, ServerToClientMessage, Companion } from "../Messages/generated/messages_pb"; import {UserMovesMessage} from "../Messages/generated/messages_pb"; import {TemplatedApp} from "uWebSockets.js" @@ -138,6 +138,14 @@ export class IoSocketController { const left = Number(query.left); const right = Number(query.right); const name = query.name; + + let companion: Companion|undefined = undefined; + + if (typeof query.companion === 'string') { + companion = new Companion(); + companion.setName(query.companion); + } + if (typeof name !== 'string') { throw new Error('Expecting name'); } @@ -221,6 +229,7 @@ export class IoSocketController { IPAddress, roomId, name, + companion, characterLayers: characterLayerObjs, messages: memberMessages, tags: memberTags, @@ -350,6 +359,7 @@ export class IoSocketController { client.tags = ws.tags; client.textures = ws.textures; client.characterLayers = ws.characterLayers; + client.companion = ws.companion; client.roomId = ws.roomId; client.listenedZones = new Set(); return client; diff --git a/pusher/src/Model/Websocket/ExSocketInterface.ts b/pusher/src/Model/Websocket/ExSocketInterface.ts index 56e7e5ca..135c6f10 100644 --- a/pusher/src/Model/Websocket/ExSocketInterface.ts +++ b/pusher/src/Model/Websocket/ExSocketInterface.ts @@ -3,6 +3,7 @@ import {Identificable} from "./Identificable"; import {ViewportInterface} from "_Model/Websocket/ViewportMessage"; import { BatchMessage, + Companion, PusherToBackMessage, ServerToClientMessage, SubMessage @@ -29,6 +30,7 @@ export interface ExSocketInterface extends WebSocket, Identificable { characterLayers: CharacterLayer[]; position: PointInterface; viewport: ViewportInterface; + companion?: Companion; /** * Pushes an event that will be sent in the next batch of events */ diff --git a/pusher/src/Model/Zone.ts b/pusher/src/Model/Zone.ts index 12d27ff3..a54481a5 100644 --- a/pusher/src/Model/Zone.ts +++ b/pusher/src/Model/Zone.ts @@ -5,7 +5,8 @@ import { CharacterLayerMessage, GroupLeftZoneMessage, GroupUpdateMessage, GroupUpdateZoneMessage, PointMessage, PositionMessage, UserJoinedMessage, UserJoinedZoneMessage, UserLeftZoneMessage, UserMovedMessage, - ZoneMessage + ZoneMessage, + Companion } from "../Messages/generated/messages_pb"; import * as messages_pb from "../Messages/generated/messages_pb"; import {ClientReadableStream} from "grpc"; @@ -30,7 +31,7 @@ export type MovesCallback = (thing: Movable, position: PositionInterface, listen export type LeavesCallback = (thing: Movable, listener: User) => void;*/ export class UserDescriptor { - private constructor(public readonly userId: number, private name: string, private characterLayers: CharacterLayerMessage[], private position: PositionMessage) { + private constructor(public readonly userId: number, private name: string, private characterLayers: CharacterLayerMessage[], private position: PositionMessage, private companion?: Companion) { if (!Number.isInteger(this.userId)) { throw new Error('UserDescriptor.userId is not an integer: '+this.userId); } @@ -41,7 +42,7 @@ export class UserDescriptor { if (position === undefined) { throw new Error('Missing position'); } - return new UserDescriptor(message.getUserid(), message.getName(), message.getCharacterlayersList(), position); + return new UserDescriptor(message.getUserid(), message.getName(), message.getCharacterlayersList(), position, message.getCompanion()); } public update(userMovedMessage: UserMovedMessage) { @@ -59,6 +60,7 @@ export class UserDescriptor { userJoinedMessage.setName(this.name); userJoinedMessage.setCharacterlayersList(this.characterLayers); userJoinedMessage.setPosition(this.position); + userJoinedMessage.setCompanion(this.companion) return userJoinedMessage; } diff --git a/pusher/src/Services/SocketManager.ts b/pusher/src/Services/SocketManager.ts index 9b698e38..d555a59c 100644 --- a/pusher/src/Services/SocketManager.ts +++ b/pusher/src/Services/SocketManager.ts @@ -153,6 +153,8 @@ export class SocketManager implements ZoneEventListener { joinRoomMessage.setName(client.name); joinRoomMessage.setPositionmessage(ProtobufUtils.toPositionMessage(client.position)); joinRoomMessage.setTagList(client.tags); + joinRoomMessage.setCompanion(client.companion); + for (const characterLayer of client.characterLayers) { const characterLayerMessage = new CharacterLayerMessage(); characterLayerMessage.setName(characterLayer.name); From fc3a503bcf1e4c35847cc89077a96adecb4fb3a9 Mon Sep 17 00:00:00 2001 From: Johannes Berthel Date: Fri, 2 Apr 2021 21:26:24 +0200 Subject: [PATCH 07/66] don't fail if companion texture is not found --- front/src/Phaser/Companion/Companion.ts | 10 ++++++---- .../CompanionTexturesLoadingManager.ts | 20 +++++++++---------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/front/src/Phaser/Companion/Companion.ts b/front/src/Phaser/Companion/Companion.ts index 196da09e..407b7326 100644 --- a/front/src/Phaser/Companion/Companion.ts +++ b/front/src/Phaser/Companion/Companion.ts @@ -36,10 +36,12 @@ export class Companion extends Container { this.companionName = name; - lazyLoadResource(this.scene.load, this.companionName).then(resource => { - this.addResource(resource); - this.invisible = false; - }) + lazyLoadResource(this.scene.load, this.companionName) + .then(resource => { + this.addResource(resource); + this.invisible = false; + }) + .catch(error => console.error(error)); this.scene.physics.world.enableBody(this); diff --git a/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts b/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts index 7023a34d..126af219 100644 --- a/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts +++ b/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts @@ -13,17 +13,17 @@ export const loadAll = (loader: LoaderPlugin): CompanionResourceDescriptionInter } export const lazyLoadResource = (loader: LoaderPlugin, name: string): Promise => { - const resource = COMPANION_RESOURCES.find(item => item.name === name); + return new Promise((resolve, reject) => { + const resource = COMPANION_RESOURCES.find(item => item.name === name); - if (typeof resource === 'undefined') { - throw new TextureError(`Texture '${name}' not found!`); - } - - if (loader.textureManager.exists(resource.name)) { - return Promise.resolve(resource.name); - } - - return new Promise(resolve => { + if (typeof resource === 'undefined') { + return reject(`Texture '${name}' not found!`); + } + + if (loader.textureManager.exists(resource.name)) { + return resolve(resource.name); + } + loader.spritesheet(resource.name, resource.img, { frameWidth: 32, frameHeight: 32, endFrame: 12 }); loader.once(`filecomplete-spritesheet-${resource.name}`, () => resolve(resource.name)); }); From e4d324e5fab5df40e621467d2fcc2cf63d4d45b3 Mon Sep 17 00:00:00 2001 From: Johannes Berthel Date: Fri, 2 Apr 2021 21:26:42 +0200 Subject: [PATCH 08/66] removed unused import --- front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts b/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts index 126af219..bc260c69 100644 --- a/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts +++ b/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts @@ -1,5 +1,4 @@ import LoaderPlugin = Phaser.Loader.LoaderPlugin; -import { TextureError } from "../../Exception/TextureError"; import { COMPANION_RESOURCES, CompanionResourceDescriptionInterface } from "./CompanionTextures"; export const loadAll = (loader: LoaderPlugin): CompanionResourceDescriptionInterface[] => { From 4cfce15695fef93e02799575cb7e4822f7c5d7c4 Mon Sep 17 00:00:00 2001 From: Johannes Berthel Date: Fri, 2 Apr 2021 21:29:45 +0200 Subject: [PATCH 09/66] only remove listener if scene was not already destroyed --- front/src/Phaser/Companion/Companion.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/front/src/Phaser/Companion/Companion.ts b/front/src/Phaser/Companion/Companion.ts index 407b7326..2d11110e 100644 --- a/front/src/Phaser/Companion/Companion.ts +++ b/front/src/Phaser/Companion/Companion.ts @@ -212,7 +212,9 @@ export class Companion extends Container { } } - this.scene.events.removeListener('update', this.step, this); + if (this.scene) { + this.scene.events.removeListener('update', this.step, this); + } super.destroy(); } From 52303c0bd69612e649acfe9a95523736e31237e2 Mon Sep 17 00:00:00 2001 From: Johannes Berthel Date: Fri, 2 Apr 2021 23:00:51 +0200 Subject: [PATCH 10/66] added select companion scene --- front/src/Connexion/LocalUserStore.ts | 9 +- .../CompanionTexturesLoadingManager.ts | 12 +- front/src/Phaser/Game/GameManager.ts | 8 + .../src/Phaser/Login/SelectCompanionScene.ts | 248 ++++++++++++++++++ front/src/index.ts | 3 +- 5 files changed, 271 insertions(+), 9 deletions(-) create mode 100644 front/src/Phaser/Login/SelectCompanionScene.ts diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts index 4989a9ea..a766f5dc 100644 --- a/front/src/Connexion/LocalUserStore.ts +++ b/front/src/Connexion/LocalUserStore.ts @@ -48,11 +48,14 @@ class LocalUserStore { return JSON.parse(localStorage.getItem(characterLayersKey) || "null"); } - setCompanion(companion: string): void { - localStorage.setItem(companionKey, companion); + setCompanion(companion: string|null): void { + return localStorage.setItem(companionKey, JSON.stringify(companion)); } getCompanion(): string|null { - return localStorage.getItem(companionKey); + return JSON.parse(localStorage.getItem(companionKey) || "null"); + } + wasCompanionSet(): boolean { + return localStorage.getItem(companionKey) ? true : false; } setGameQualityValue(value: number): void { diff --git a/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts b/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts index bc260c69..b7859a70 100644 --- a/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts +++ b/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts @@ -1,14 +1,16 @@ import LoaderPlugin = Phaser.Loader.LoaderPlugin; import { COMPANION_RESOURCES, CompanionResourceDescriptionInterface } from "./CompanionTextures"; -export const loadAll = (loader: LoaderPlugin): CompanionResourceDescriptionInterface[] => { - const resources = COMPANION_RESOURCES; +export const loadAll = (loader: LoaderPlugin): Promise => { + const promises: Promise[] = []; - resources.forEach((resource: CompanionResourceDescriptionInterface) => { - loader.spritesheet(resource.name, resource.img, { frameWidth: 32, frameHeight: 32, endFrame: 12 }); + COMPANION_RESOURCES.forEach((resource: CompanionResourceDescriptionInterface) => { + promises.push(lazyLoadResource(loader, resource.name)); }); - return resources; + return Promise.all(promises).then(() => { + return COMPANION_RESOURCES; + }); } export const lazyLoadResource = (loader: LoaderPlugin, name: string): Promise => { diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts index fbe5102c..70625b24 100644 --- a/front/src/Phaser/Game/GameManager.ts +++ b/front/src/Phaser/Game/GameManager.ts @@ -5,6 +5,7 @@ import {MenuScene, MenuSceneName} from "../Menu/MenuScene"; import {HelpCameraSettingsScene, HelpCameraSettingsSceneName} from "../Menu/HelpCameraSettingsScene"; import {LoginSceneName} from "../Login/LoginScene"; import {SelectCharacterSceneName} from "../Login/SelectCharacterScene"; +import {SelectCompanionSceneName} from "../Login/SelectCompanionScene"; import {EnableCameraSceneName} from "../Login/EnableCameraScene"; import {localUserStore} from "../../Connexion/LocalUserStore"; @@ -39,6 +40,8 @@ export class GameManager { return LoginSceneName; } else if (!this.characterLayers) { return SelectCharacterSceneName; + } else if (!localUserStore.wasCompanionSet()) { + return SelectCompanionSceneName; } else { return EnableCameraSceneName; } @@ -65,6 +68,11 @@ export class GameManager { return this.characterLayers; } + + setCompanion(companion: string|null): void { + this.companion = companion; + } + getCompanion(): string|null { return this.companion; } diff --git a/front/src/Phaser/Login/SelectCompanionScene.ts b/front/src/Phaser/Login/SelectCompanionScene.ts new file mode 100644 index 00000000..9b1cb09a --- /dev/null +++ b/front/src/Phaser/Login/SelectCompanionScene.ts @@ -0,0 +1,248 @@ +import Image = Phaser.GameObjects.Image; +import Rectangle = Phaser.GameObjects.Rectangle; +import {gameManager} from "../Game/GameManager"; +import { addLoader } from "../Components/Loader"; +import {TextField} from "../Components/TextField"; +import { ResizableScene } from "./ResizableScene"; +import {EnableCameraSceneName} from "./EnableCameraScene"; +import {localUserStore} from "../../Connexion/LocalUserStore"; +import { loadAll } from "../Companion/CompanionTexturesLoadingManager"; +import { CompanionResourceDescriptionInterface } from "../Companion/CompanionTextures"; + +export const SelectCompanionSceneName = "SelectCompanionScene"; + +enum LoginTextures { + playButton = "play_button", + icon = "icon", + mainFont = "main_font" +} + +export class SelectCompanionScene extends ResizableScene { + private logo!: Image; + private textField!: TextField; + private pressReturnField!: TextField; + private readonly nbCharactersPerRow = 7; + + private selectedRectangle!: Rectangle; + private selectedRectangleXPos = 0; + private selectedRectangleYPos = 0; + + private selectedCompanion!: Phaser.Physics.Arcade.Sprite; + private companions: Array = new Array(); + private companionModels: Array = [null]; + + constructor() { + super({ + key: SelectCompanionSceneName + }); + } + + preload() { + loadAll(this.load).then(resourceDescriptions => { + resourceDescriptions.forEach(resourceDescription => { + this.companionModels.push(resourceDescription); + }); + }); + + this.load.image(LoginTextures.icon, "resources/logos/tcm_full.png"); + this.load.image(LoginTextures.playButton, "resources/objects/play_button.png"); + + // Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap + this.load.bitmapFont(LoginTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml'); + + addLoader(this); + } + + create() { + this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Select your companion'); + + this.pressReturnField = new TextField( + this, + this.game.renderer.width / 2, + 90 + 32 * Math.ceil(this.companionModels.length / this.nbCharactersPerRow), + 'Press enter to start' + ); + + const rectangleXStart = this.game.renderer.width / 2 - (this.nbCharactersPerRow / 2) * 32 + 16; + this.selectedRectangle = this.add.rectangle(rectangleXStart, 90, 32, 32).setStrokeStyle(2, 0xFFFFFF); + + this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, LoginTextures.icon); + this.add.existing(this.logo); + + // input events + this.input.keyboard.on('keyup-ENTER', () => { + return this.nextScene(); + }); + + this.input.keyboard.on('keydown-RIGHT', () => { + if(this.selectedRectangleYPos * this.nbCharactersPerRow + (this.selectedRectangleXPos + 2)) + if ( + this.selectedRectangleXPos < this.nbCharactersPerRow - 1 + && ((this.selectedRectangleYPos * this.nbCharactersPerRow) + (this.selectedRectangleXPos + 1) + 1) <= this.companionModels.length + ) { + this.selectedRectangleXPos++; + } + this.updateSelectedCompanion(); + }); + + this.input.keyboard.on('keydown-LEFT', () => { + if ( + this.selectedRectangleXPos > 0 + && ((this.selectedRectangleYPos * this.nbCharactersPerRow) + (this.selectedRectangleXPos - 1) + 1) <= this.companionModels.length + ) { + this.selectedRectangleXPos--; + } + this.updateSelectedCompanion(); + }); + + this.input.keyboard.on('keydown-DOWN', () => { + if ( + this.selectedRectangleYPos + 1 < Math.ceil(this.companionModels.length / this.nbCharactersPerRow) + && ( + (((this.selectedRectangleYPos + 1) * this.nbCharactersPerRow) + this.selectedRectangleXPos + 1) <= this.companionModels.length // check if companion isn't empty + || (this.selectedRectangleYPos + 1) === Math.ceil(this.companionModels.length / this.nbCharactersPerRow) // check if is custom rectangle + ) + ) { + this.selectedRectangleYPos++; + } + this.updateSelectedCompanion(); + }); + + this.input.keyboard.on('keydown-UP', () => { + if ( + this.selectedRectangleYPos > 0 + && (((this.selectedRectangleYPos - 1) * this.nbCharactersPerRow) + this.selectedRectangleXPos + 1) <= this.companionModels.length + ) { + this.selectedRectangleYPos--; + } + this.updateSelectedCompanion(); + }); + + this.createCurrentCompanion(); + + const companionNumber = this.getCompanionIndex(); + + this.selectedRectangleXPos = companionNumber % this.nbCharactersPerRow; + this.selectedRectangleYPos = Math.floor(companionNumber / this.nbCharactersPerRow); + + this.updateSelectedCompanion(); + } + + update(time: number, delta: number): void { + this.pressReturnField.setVisible(!!(Math.floor(time / 500) % 2)); + } + + private nextScene(): void { + // store companion + const companionNumber = this.selectedRectangleXPos + this.selectedRectangleYPos * this.nbCharactersPerRow; + const model = this.companionModels[companionNumber]; + const companion = model === null ? null : model.name; + + localUserStore.setCompanion(companion); + + // next scene + this.scene.stop(SelectCompanionSceneName); + + gameManager.setCompanion(companion); + gameManager.tryResumingGame(this, EnableCameraSceneName); + + this.scene.remove(SelectCompanionSceneName); + } + + private createCurrentCompanion(): void { + for (let i = 0; i < this.companionModels.length; i++) { + const companionResource = this.companionModels[i]; + + const col = i % this.nbCharactersPerRow; + const row = Math.floor(i / this.nbCharactersPerRow); + + const [x, y] = this.getCharacterPosition(col, row); + + let name = "null"; + if (companionResource !== null) { + name = companionResource.name; + } + + const companion = this.physics.add.sprite(x, y, name, 0); + companion.setBounce(0.2); + companion.setCollideWorldBounds(true); + + if (companionResource !== null) { + this.anims.create({ + key: name, + frames: this.anims.generateFrameNumbers(name, {start: 0, end: 2,}), + frameRate: 10, + repeat: -1 + }); + } + + companion.setInteractive().on("pointerdown", () => { + this.selectedRectangleXPos = col; + this.selectedRectangleYPos = row; + this.updateSelectedCompanion(); + }); + + this.companions.push(companion); + } + + this.selectedCompanion = this.companions[0]; + } + + private getCharacterPosition(x: number, y: number): [number, number] { + return [ + this.game.renderer.width / 2 + 16 + (x - this.nbCharactersPerRow / 2) * 32, + y * 32 + 90 + ]; + } + + private updateSelectedCompanion(): void { + this.selectedCompanion?.anims.pause(); + + const [x, y] = this.getCharacterPosition(this.selectedRectangleXPos, this.selectedRectangleYPos); + this.selectedRectangle.setVisible(true); + this.selectedRectangle.setX(x); + this.selectedRectangle.setY(y); + this.selectedRectangle.setSize(32, 32); + + const companionNumber = this.selectedRectangleXPos + this.selectedRectangleYPos * this.nbCharactersPerRow; + const model = this.companionModels[companionNumber]; + + const companion = this.companions[companionNumber]; + + if (model !== null) { + companion.play(model.name); + } + + this.selectedCompanion = companion; + } + + public onResize(ev: UIEvent): void { + this.textField.x = this.game.renderer.width / 2; + this.pressReturnField.x = this.game.renderer.width / 2; + this.logo.x = this.game.renderer.width - 30; + this.logo.y = this.game.renderer.height - 20; + + for (let i = 0; i model !== null && model.name === companion); + } +} diff --git a/front/src/index.ts b/front/src/index.ts index c0663acd..aab45a9b 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -6,6 +6,7 @@ import {DEBUG_MODE, JITSI_URL, RESOLUTION} from "./Enum/EnvironmentVariable"; import {LoginScene} from "./Phaser/Login/LoginScene"; import {ReconnectingScene} from "./Phaser/Reconnecting/ReconnectingScene"; import {SelectCharacterScene} from "./Phaser/Login/SelectCharacterScene"; +import {SelectCompanionScene} from "./Phaser/Login/SelectCompanionScene"; import {EnableCameraScene} from "./Phaser/Login/EnableCameraScene"; import {CustomizeScene} from "./Phaser/Login/CustomizeScene"; import {ResizableScene} from "./Phaser/Login/ResizableScene"; @@ -74,7 +75,7 @@ const config: GameConfig = { width: width / RESOLUTION, height: height / RESOLUTION, parent: "game", - scene: [EntryScene, LoginScene, SelectCharacterScene, EnableCameraScene, ReconnectingScene, ErrorScene, CustomizeScene, MenuScene, HelpCameraSettingsScene], + scene: [EntryScene, LoginScene, SelectCharacterScene, SelectCompanionScene, EnableCameraScene, ReconnectingScene, ErrorScene, CustomizeScene, MenuScene, HelpCameraSettingsScene], zoom: RESOLUTION, fps: fps, dom: { From 6ee488977370e6abca60a05762ea98e8d0ae3947 Mon Sep 17 00:00:00 2001 From: Johannes Berthel Date: Fri, 2 Apr 2021 23:13:03 +0200 Subject: [PATCH 11/66] added change companion button to menu --- front/dist/resources/html/gameMenu.html | 3 +++ .../CompanionTexturesLoadingManager.ts | 6 +++++- .../src/Phaser/Login/SelectCompanionScene.ts | 20 ++++++++++--------- front/src/Phaser/Menu/MenuScene.ts | 5 +++++ 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/front/dist/resources/html/gameMenu.html b/front/dist/resources/html/gameMenu.html index f0faf5c5..d5e9ad7e 100644 --- a/front/dist/resources/html/gameMenu.html +++ b/front/dist/resources/html/gameMenu.html @@ -30,6 +30,9 @@
+
+ +
diff --git a/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts b/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts index b7859a70..1c74b64f 100644 --- a/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts +++ b/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts @@ -1,7 +1,11 @@ import LoaderPlugin = Phaser.Loader.LoaderPlugin; import { COMPANION_RESOURCES, CompanionResourceDescriptionInterface } from "./CompanionTextures"; -export const loadAll = (loader: LoaderPlugin): Promise => { +export const getAllResources = (): CompanionResourceDescriptionInterface[] => { + return COMPANION_RESOURCES; +} + +export const lazyLoadAllResources = (loader: LoaderPlugin): Promise => { const promises: Promise[] = []; COMPANION_RESOURCES.forEach((resource: CompanionResourceDescriptionInterface) => { diff --git a/front/src/Phaser/Login/SelectCompanionScene.ts b/front/src/Phaser/Login/SelectCompanionScene.ts index 9b1cb09a..5a6cc160 100644 --- a/front/src/Phaser/Login/SelectCompanionScene.ts +++ b/front/src/Phaser/Login/SelectCompanionScene.ts @@ -1,13 +1,13 @@ import Image = Phaser.GameObjects.Image; import Rectangle = Phaser.GameObjects.Rectangle; -import {gameManager} from "../Game/GameManager"; import { addLoader } from "../Components/Loader"; -import {TextField} from "../Components/TextField"; +import { gameManager} from "../Game/GameManager"; import { ResizableScene } from "./ResizableScene"; -import {EnableCameraSceneName} from "./EnableCameraScene"; -import {localUserStore} from "../../Connexion/LocalUserStore"; -import { loadAll } from "../Companion/CompanionTexturesLoadingManager"; +import { TextField } from "../Components/TextField"; +import { EnableCameraSceneName } from "./EnableCameraScene"; +import { localUserStore } from "../../Connexion/LocalUserStore"; import { CompanionResourceDescriptionInterface } from "../Companion/CompanionTextures"; +import { getAllResources, lazyLoadAllResources } from "../Companion/CompanionTexturesLoadingManager"; export const SelectCompanionSceneName = "SelectCompanionScene"; @@ -38,10 +38,12 @@ export class SelectCompanionScene extends ResizableScene { } preload() { - loadAll(this.load).then(resourceDescriptions => { - resourceDescriptions.forEach(resourceDescription => { - this.companionModels.push(resourceDescription); - }); + lazyLoadAllResources(this.load).then(() => { + console.log("Loaded all companion textures."); + }); + + getAllResources().forEach(model => { + this.companionModels.push(model); }); this.load.image(LoginTextures.icon, "resources/logos/tcm_full.png"); diff --git a/front/src/Phaser/Menu/MenuScene.ts b/front/src/Phaser/Menu/MenuScene.ts index 58e7f0a6..f29fd39d 100644 --- a/front/src/Phaser/Menu/MenuScene.ts +++ b/front/src/Phaser/Menu/MenuScene.ts @@ -1,5 +1,6 @@ import {LoginScene, LoginSceneName} from "../Login/LoginScene"; import {SelectCharacterScene, SelectCharacterSceneName} from "../Login/SelectCharacterScene"; +import {SelectCompanionScene, SelectCompanionSceneName} from "../Login/SelectCompanionScene"; import {gameManager} from "../Game/GameManager"; import {localUserStore} from "../../Connexion/LocalUserStore"; import {mediaManager} from "../../WebRtc/MediaManager"; @@ -277,6 +278,10 @@ export class MenuScene extends Phaser.Scene { this.closeSideMenu(); gameManager.leaveGame(this, SelectCharacterSceneName, new SelectCharacterScene()); break; + case 'changeCompanionButton': + this.closeSideMenu(); + gameManager.leaveGame(this, SelectCompanionSceneName, new SelectCompanionScene()); + break; case 'closeButton': this.closeSideMenu(); break; From 38c06ce8ff634edb387f0e480ee0e3a9e39fc11b Mon Sep 17 00:00:00 2001 From: Johannes Berthel Date: Fri, 2 Apr 2021 23:17:04 +0200 Subject: [PATCH 12/66] navigate from select character scene to select companion scene --- front/src/Phaser/Login/SelectCharacterScene.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index e47cf38a..1d595494 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -2,6 +2,7 @@ import {gameManager} from "../Game/GameManager"; import {TextField} from "../Components/TextField"; import Image = Phaser.GameObjects.Image; import Rectangle = Phaser.GameObjects.Rectangle; +import {SelectCompanionSceneName} from "./SelectCompanionScene"; import {EnableCameraSceneName} from "./EnableCameraScene"; import {CustomizeSceneName} from "./CustomizeScene"; import {ResizableScene} from "./ResizableScene"; @@ -145,7 +146,7 @@ export class SelectCharacterScene extends AbstractCharacterScene { this.scene.stop(SelectCharacterSceneName); if (this.selectedPlayer !== null) { gameManager.setCharacterLayers([this.selectedPlayer.texture.key]); - gameManager.tryResumingGame(this, EnableCameraSceneName); + gameManager.tryResumingGame(this, localUserStore.wasCompanionSet() ? EnableCameraSceneName : SelectCompanionSceneName); } else { this.scene.run(CustomizeSceneName); } From 3187520e7b4bc8c8b54098e6b6bc0496d0483b3f Mon Sep 17 00:00:00 2001 From: Johannes Berthel Date: Fri, 2 Apr 2021 23:36:06 +0200 Subject: [PATCH 13/66] companion behaviour fine tuning --- front/src/Phaser/Companion/Companion.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/front/src/Phaser/Companion/Companion.ts b/front/src/Phaser/Companion/Companion.ts index 2d11110e..cb932cbb 100644 --- a/front/src/Phaser/Companion/Companion.ts +++ b/front/src/Phaser/Companion/Companion.ts @@ -23,7 +23,7 @@ export class Companion extends Container { private animationType: PlayerAnimationTypes; constructor(scene: Phaser.Scene, x: number, y: number, name: string) { - super(scene, x + 8, y + 8); + super(scene, x + 14, y + 4); this.sprites = new Map(); @@ -59,7 +59,7 @@ export class Companion extends Container { } public setTarget(x: number, y: number, direction: PlayerAnimationDirections) { - this.target = { x, y, direction }; + this.target = { x, y: y + 4, direction }; } public step(time: number, delta: number) { @@ -76,7 +76,7 @@ export class Companion extends Container { const distance = Math.pow(xDist, 2) + Math.pow(yDist, 2); - if (distance < 576) { // 24^2 + if (distance < 650) { this.animationType = PlayerAnimationTypes.Idle; this.direction = this.target.direction; @@ -88,7 +88,7 @@ export class Companion extends Container { const yDir = yDist / Math.max(Math.abs(yDist), 1); const speed = 256; - this.getBody().setVelocity(Math.min(Math.abs(xDist * 2), speed) * xDir, Math.min(Math.abs(yDist * 2), speed) * yDir); + this.getBody().setVelocity(Math.min(Math.abs(xDist * 2.5), speed) * xDir, Math.min(Math.abs(yDist * 2.5), speed) * yDir); if (Math.abs(xDist) > Math.abs(yDist)) { if (xDist < 0) { From 8c35860f2abde025bcbe66ea2978f2f89d4147bd Mon Sep 17 00:00:00 2001 From: Johannes Berthel Date: Fri, 2 Apr 2021 23:49:25 +0200 Subject: [PATCH 14/66] show select companion scene after customize scene --- front/src/Phaser/Login/CustomizeScene.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 4f5b2860..87e5aac9 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -1,4 +1,5 @@ import {EnableCameraSceneName} from "./EnableCameraScene"; +import {SelectCompanionSceneName} from "./SelectCompanionScene"; import {TextField} from "../Components/TextField"; import Image = Phaser.GameObjects.Image; import Rectangle = Phaser.GameObjects.Rectangle; @@ -115,7 +116,7 @@ export class CustomizeScene extends AbstractCharacterScene { gameManager.setCharacterLayers(layers); this.scene.sleep(CustomizeSceneName); - gameManager.tryResumingGame(this, EnableCameraSceneName); + gameManager.tryResumingGame(this, localUserStore.wasCompanionSet() ? EnableCameraSceneName : SelectCompanionSceneName); }); this.input.keyboard.on('keyup-RIGHT', () => this.moveCursorHorizontally(1)); From 6c512fb7cd9d4b0f9391489b9e0919cba38ea88a Mon Sep 17 00:00:00 2001 From: Johannes Berthel Date: Sat, 3 Apr 2021 00:18:08 +0200 Subject: [PATCH 15/66] fixed linting --- front/src/Phaser/Companion/Companion.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/front/src/Phaser/Companion/Companion.ts b/front/src/Phaser/Companion/Companion.ts index cb932cbb..e578dd3d 100644 --- a/front/src/Phaser/Companion/Companion.ts +++ b/front/src/Phaser/Companion/Companion.ts @@ -16,6 +16,7 @@ export class Companion extends Container { private delta: number; private invisible: boolean; + private updateListener: Function; private target: { x: number, y: number, direction: PlayerAnimationDirections }; private companionName: string; @@ -53,7 +54,8 @@ export class Companion extends Container { this.setDepth(-1); - this.scene.events.addListener('update', this.step, this); + this.updateListener = this.step.bind(this); + this.scene.events.addListener('update', this.updateListener); this.scene.add.existing(this); } @@ -213,7 +215,7 @@ export class Companion extends Container { } if (this.scene) { - this.scene.events.removeListener('update', this.step, this); + this.scene.events.removeListener('update', this.updateListener); } super.destroy(); From 7c6b73efdbc8b7d178b93c4d26c1d8c900bf990a Mon Sep 17 00:00:00 2001 From: Johannes Berthel Date: Tue, 6 Apr 2021 18:36:46 +0200 Subject: [PATCH 16/66] don't show companion selection during first connexion flow --- front/src/Phaser/Login/CustomizeScene.ts | 3 +-- front/src/Phaser/Login/SelectCharacterScene.ts | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 87e5aac9..4f5b2860 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -1,5 +1,4 @@ import {EnableCameraSceneName} from "./EnableCameraScene"; -import {SelectCompanionSceneName} from "./SelectCompanionScene"; import {TextField} from "../Components/TextField"; import Image = Phaser.GameObjects.Image; import Rectangle = Phaser.GameObjects.Rectangle; @@ -116,7 +115,7 @@ export class CustomizeScene extends AbstractCharacterScene { gameManager.setCharacterLayers(layers); this.scene.sleep(CustomizeSceneName); - gameManager.tryResumingGame(this, localUserStore.wasCompanionSet() ? EnableCameraSceneName : SelectCompanionSceneName); + gameManager.tryResumingGame(this, EnableCameraSceneName); }); this.input.keyboard.on('keyup-RIGHT', () => this.moveCursorHorizontally(1)); diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index 1d595494..e47cf38a 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -2,7 +2,6 @@ import {gameManager} from "../Game/GameManager"; import {TextField} from "../Components/TextField"; import Image = Phaser.GameObjects.Image; import Rectangle = Phaser.GameObjects.Rectangle; -import {SelectCompanionSceneName} from "./SelectCompanionScene"; import {EnableCameraSceneName} from "./EnableCameraScene"; import {CustomizeSceneName} from "./CustomizeScene"; import {ResizableScene} from "./ResizableScene"; @@ -146,7 +145,7 @@ export class SelectCharacterScene extends AbstractCharacterScene { this.scene.stop(SelectCharacterSceneName); if (this.selectedPlayer !== null) { gameManager.setCharacterLayers([this.selectedPlayer.texture.key]); - gameManager.tryResumingGame(this, localUserStore.wasCompanionSet() ? EnableCameraSceneName : SelectCompanionSceneName); + gameManager.tryResumingGame(this, EnableCameraSceneName); } else { this.scene.run(CustomizeSceneName); } From e5a196a42bdbe0b601c139ddef9cde9cec6fc78c Mon Sep 17 00:00:00 2001 From: Johannes Berthel Date: Tue, 6 Apr 2021 18:54:45 +0200 Subject: [PATCH 17/66] implemented feedback --- back/src/Model/User.ts | 4 ++-- front/src/Connexion/LocalUserStore.ts | 8 +++++++- front/src/Phaser/Companion/Companion.ts | 4 ++-- .../CompanionTexturesLoadingManager.ts | 20 ++++++------------- .../src/Phaser/Login/SelectCompanionScene.ts | 8 +++----- messages/protos/messages.proto | 8 ++++---- pusher/src/Controller/IoSocketController.ts | 6 +++--- .../src/Model/Websocket/ExSocketInterface.ts | 4 ++-- pusher/src/Model/Zone.ts | 4 ++-- 9 files changed, 31 insertions(+), 35 deletions(-) diff --git a/back/src/Model/User.ts b/back/src/Model/User.ts index 370781e5..52a96b61 100644 --- a/back/src/Model/User.ts +++ b/back/src/Model/User.ts @@ -4,7 +4,7 @@ import {Zone} from "_Model/Zone"; import {Movable} from "_Model/Movable"; import {PositionNotifier} from "_Model/PositionNotifier"; import {ServerDuplexStream} from "grpc"; -import {BatchMessage, Companion, PusherToBackMessage, ServerToClientMessage, SubMessage} from "../Messages/generated/messages_pb"; +import {BatchMessage, CompanionMessage, PusherToBackMessage, ServerToClientMessage, SubMessage} from "../Messages/generated/messages_pb"; import {CharacterLayer} from "_Model/Websocket/CharacterLayer"; export type UserSocket = ServerDuplexStream; @@ -24,7 +24,7 @@ export class User implements Movable { public readonly tags: string[], public readonly name: string, public readonly characterLayers: CharacterLayer[], - public readonly companion?: Companion + public readonly companion?: CompanionMessage ) { this.listenedZones = new Set(); diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts index a766f5dc..74e45049 100644 --- a/front/src/Connexion/LocalUserStore.ts +++ b/front/src/Connexion/LocalUserStore.ts @@ -52,7 +52,13 @@ class LocalUserStore { return localStorage.setItem(companionKey, JSON.stringify(companion)); } getCompanion(): string|null { - return JSON.parse(localStorage.getItem(companionKey) || "null"); + const companion = JSON.parse(localStorage.getItem(companionKey) || "null"); + + if (typeof companion !== "string" || companion === "") { + return null; + } + + return companion; } wasCompanionSet(): boolean { return localStorage.getItem(companionKey) ? true : false; diff --git a/front/src/Phaser/Companion/Companion.ts b/front/src/Phaser/Companion/Companion.ts index e578dd3d..48a3fa4b 100644 --- a/front/src/Phaser/Companion/Companion.ts +++ b/front/src/Phaser/Companion/Companion.ts @@ -1,6 +1,6 @@ import Sprite = Phaser.GameObjects.Sprite; import Container = Phaser.GameObjects.Container; -import { lazyLoadResource } from "./CompanionTexturesLoadingManager"; +import { lazyLoadCompanionResource } from "./CompanionTexturesLoadingManager"; import { PlayerAnimationDirections, PlayerAnimationTypes } from "../Player/Animation"; export interface CompanionStatus { @@ -37,7 +37,7 @@ export class Companion extends Container { this.companionName = name; - lazyLoadResource(this.scene.load, this.companionName) + lazyLoadCompanionResource(this.scene.load, this.companionName) .then(resource => { this.addResource(resource); this.invisible = false; diff --git a/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts b/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts index 1c74b64f..354c5740 100644 --- a/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts +++ b/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts @@ -1,23 +1,15 @@ import LoaderPlugin = Phaser.Loader.LoaderPlugin; import { COMPANION_RESOURCES, CompanionResourceDescriptionInterface } from "./CompanionTextures"; -export const getAllResources = (): CompanionResourceDescriptionInterface[] => { +export const getAllCompanionResources = (loader: LoaderPlugin): CompanionResourceDescriptionInterface[] => { + COMPANION_RESOURCES.forEach((resource: CompanionResourceDescriptionInterface) => { + lazyLoadCompanionResource(loader, resource.name); + }); + return COMPANION_RESOURCES; } -export const lazyLoadAllResources = (loader: LoaderPlugin): Promise => { - const promises: Promise[] = []; - - COMPANION_RESOURCES.forEach((resource: CompanionResourceDescriptionInterface) => { - promises.push(lazyLoadResource(loader, resource.name)); - }); - - return Promise.all(promises).then(() => { - return COMPANION_RESOURCES; - }); -} - -export const lazyLoadResource = (loader: LoaderPlugin, name: string): Promise => { +export const lazyLoadCompanionResource = (loader: LoaderPlugin, name: string): Promise => { return new Promise((resolve, reject) => { const resource = COMPANION_RESOURCES.find(item => item.name === name); diff --git a/front/src/Phaser/Login/SelectCompanionScene.ts b/front/src/Phaser/Login/SelectCompanionScene.ts index 5a6cc160..3d20d35a 100644 --- a/front/src/Phaser/Login/SelectCompanionScene.ts +++ b/front/src/Phaser/Login/SelectCompanionScene.ts @@ -7,7 +7,7 @@ import { TextField } from "../Components/TextField"; import { EnableCameraSceneName } from "./EnableCameraScene"; import { localUserStore } from "../../Connexion/LocalUserStore"; import { CompanionResourceDescriptionInterface } from "../Companion/CompanionTextures"; -import { getAllResources, lazyLoadAllResources } from "../Companion/CompanionTexturesLoadingManager"; +import { getAllCompanionResources } from "../Companion/CompanionTexturesLoadingManager"; export const SelectCompanionSceneName = "SelectCompanionScene"; @@ -38,11 +38,9 @@ export class SelectCompanionScene extends ResizableScene { } preload() { - lazyLoadAllResources(this.load).then(() => { - console.log("Loaded all companion textures."); - }); + addLoader(this); - getAllResources().forEach(model => { + getAllCompanionResources(this.load).forEach(model => { this.companionModels.push(model); }); diff --git a/messages/protos/messages.proto b/messages/protos/messages.proto index 372f00b7..b3d4e755 100644 --- a/messages/protos/messages.proto +++ b/messages/protos/messages.proto @@ -36,7 +36,7 @@ message CharacterLayerMessage { string name = 2; } -message Companion { +message CompanionMessage { string name = 1; } @@ -145,7 +145,7 @@ message UserJoinedMessage { string name = 2; repeated CharacterLayerMessage characterLayers = 3; PositionMessage position = 4; - Companion companion = 5; + CompanionMessage companion = 5; } message UserLeftMessage { @@ -256,7 +256,7 @@ message JoinRoomMessage { string roomId = 5; repeated string tag = 6; string IPAddress = 7; - Companion companion = 8; + CompanionMessage companion = 8; } message UserJoinedZoneMessage { @@ -265,7 +265,7 @@ message UserJoinedZoneMessage { repeated CharacterLayerMessage characterLayers = 3; PositionMessage position = 4; Zone fromZone = 5; - Companion companion = 6; + CompanionMessage companion = 6; } message UserLeftZoneMessage { diff --git a/pusher/src/Controller/IoSocketController.ts b/pusher/src/Controller/IoSocketController.ts index 2079548f..87051bbc 100644 --- a/pusher/src/Controller/IoSocketController.ts +++ b/pusher/src/Controller/IoSocketController.ts @@ -12,7 +12,7 @@ import { WebRtcSignalToServerMessage, PlayGlobalMessage, ReportPlayerMessage, - QueryJitsiJwtMessage, SendUserMessage, ServerToClientMessage, Companion + QueryJitsiJwtMessage, SendUserMessage, ServerToClientMessage, CompanionMessage } from "../Messages/generated/messages_pb"; import {UserMovesMessage} from "../Messages/generated/messages_pb"; import {TemplatedApp} from "uWebSockets.js" @@ -139,10 +139,10 @@ export class IoSocketController { const right = Number(query.right); const name = query.name; - let companion: Companion|undefined = undefined; + let companion: CompanionMessage|undefined = undefined; if (typeof query.companion === 'string') { - companion = new Companion(); + companion = new CompanionMessage(); companion.setName(query.companion); } diff --git a/pusher/src/Model/Websocket/ExSocketInterface.ts b/pusher/src/Model/Websocket/ExSocketInterface.ts index 135c6f10..5b9a4f7e 100644 --- a/pusher/src/Model/Websocket/ExSocketInterface.ts +++ b/pusher/src/Model/Websocket/ExSocketInterface.ts @@ -3,7 +3,7 @@ import {Identificable} from "./Identificable"; import {ViewportInterface} from "_Model/Websocket/ViewportMessage"; import { BatchMessage, - Companion, + CompanionMessage, PusherToBackMessage, ServerToClientMessage, SubMessage @@ -30,7 +30,7 @@ export interface ExSocketInterface extends WebSocket, Identificable { characterLayers: CharacterLayer[]; position: PointInterface; viewport: ViewportInterface; - companion?: Companion; + companion?: CompanionMessage; /** * Pushes an event that will be sent in the next batch of events */ diff --git a/pusher/src/Model/Zone.ts b/pusher/src/Model/Zone.ts index a54481a5..3f39a5ed 100644 --- a/pusher/src/Model/Zone.ts +++ b/pusher/src/Model/Zone.ts @@ -6,7 +6,7 @@ import { PointMessage, PositionMessage, UserJoinedMessage, UserJoinedZoneMessage, UserLeftZoneMessage, UserMovedMessage, ZoneMessage, - Companion + CompanionMessage } from "../Messages/generated/messages_pb"; import * as messages_pb from "../Messages/generated/messages_pb"; import {ClientReadableStream} from "grpc"; @@ -31,7 +31,7 @@ export type MovesCallback = (thing: Movable, position: PositionInterface, listen export type LeavesCallback = (thing: Movable, listener: User) => void;*/ export class UserDescriptor { - private constructor(public readonly userId: number, private name: string, private characterLayers: CharacterLayerMessage[], private position: PositionMessage, private companion?: Companion) { + private constructor(public readonly userId: number, private name: string, private characterLayers: CharacterLayerMessage[], private position: PositionMessage, private companion?: CompanionMessage) { if (!Number.isInteger(this.userId)) { throw new Error('UserDescriptor.userId is not an integer: '+this.userId); } From 187e21eed98c3b017eb366aa019920203c3207f0 Mon Sep 17 00:00:00 2001 From: Johannes Berthel Date: Tue, 6 Apr 2021 19:10:18 +0200 Subject: [PATCH 18/66] load texture inside game scene instead inside of inside companion class --- front/src/Phaser/Companion/Companion.ts | 12 +++++------- front/src/Phaser/Entity/Character.ts | 6 ++++-- front/src/Phaser/Entity/RemotePlayer.ts | 5 +++-- front/src/Phaser/Game/GameScene.ts | 7 +++++-- front/src/Phaser/Player/Player.ts | 5 +++-- 5 files changed, 20 insertions(+), 15 deletions(-) diff --git a/front/src/Phaser/Companion/Companion.ts b/front/src/Phaser/Companion/Companion.ts index 48a3fa4b..72491ae1 100644 --- a/front/src/Phaser/Companion/Companion.ts +++ b/front/src/Phaser/Companion/Companion.ts @@ -23,7 +23,7 @@ export class Companion extends Container { private direction: PlayerAnimationDirections; private animationType: PlayerAnimationTypes; - constructor(scene: Phaser.Scene, x: number, y: number, name: string) { + constructor(scene: Phaser.Scene, x: number, y: number, name: string, texturePromise: Promise) { super(scene, x + 14, y + 4); this.sprites = new Map(); @@ -37,12 +37,10 @@ export class Companion extends Container { this.companionName = name; - lazyLoadCompanionResource(this.scene.load, this.companionName) - .then(resource => { - this.addResource(resource); - this.invisible = false; - }) - .catch(error => console.error(error)); + texturePromise.then(resource => { + this.addResource(resource); + this.invisible = false; + }) this.scene.physics.world.enableBody(this); diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index eb8e45fd..5b541267 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -71,8 +71,10 @@ export abstract class Character extends Container { this.playAnimation(direction, moving); } - public addCompanion(name: string): void { - this.companion = new Companion(this.scene, this.x, this.y, name); + public addCompanion(name: string, texturePromise?: Promise): void { + if (typeof texturePromise !== 'undefined') { + this.companion = new Companion(this.scene, this.x, this.y, name, texturePromise); + } } public addTextures(textures: string[], frame?: string | number): void { diff --git a/front/src/Phaser/Entity/RemotePlayer.ts b/front/src/Phaser/Entity/RemotePlayer.ts index b405d8df..41e2e2df 100644 --- a/front/src/Phaser/Entity/RemotePlayer.ts +++ b/front/src/Phaser/Entity/RemotePlayer.ts @@ -18,7 +18,8 @@ export class RemotePlayer extends Character { texturesPromise: Promise, direction: PlayerAnimationDirections, moving: boolean, - companion: string|null + companion: string|null, + companionTexturePromise?: Promise ) { super(Scene, x, y, texturesPromise, name, direction, moving, 1); @@ -26,7 +27,7 @@ export class RemotePlayer extends Character { this.userId = userId; if (typeof companion === 'string') { - this.addCompanion(companion); + this.addCompanion(companion, companionTexturePromise); } } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 222f7ed5..7cbefb39 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -69,6 +69,7 @@ import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR; import DOMElement = Phaser.GameObjects.DOMElement; import {Subscription} from "rxjs"; import {worldFullMessageStream} from "../../Connexion/WorldFullMessageStream"; +import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager"; export interface GameSceneInitInterface { initPosition: PointInterface|null, @@ -1024,7 +1025,8 @@ ${escapedMessage} PlayerAnimationDirections.Down, false, this.userInputManager, - this.companion + this.companion, + this.companion !== null ? lazyLoadCompanionResource(this.load, this.companion) : undefined ); }catch (err){ if(err instanceof TextureError) { @@ -1217,7 +1219,8 @@ ${escapedMessage} texturesPromise, addPlayerData.position.direction as PlayerAnimationDirections, addPlayerData.position.moving, - addPlayerData.companion + addPlayerData.companion, + addPlayerData.companion !== null ? lazyLoadCompanionResource(this.load, addPlayerData.companion) : undefined ); this.MapPlayers.add(player); this.MapPlayersByKey.set(player.userId, player); diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index d018a41f..bb961115 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -22,7 +22,8 @@ export class Player extends Character implements CurrentGamerInterface { direction: PlayerAnimationDirections, moving: boolean, private userInputManager: UserInputManager, - companion: string|null + companion: string|null, + companionTexturePromise?: Promise ) { super(Scene, x, y, texturesPromise, name, direction, moving, 1); @@ -30,7 +31,7 @@ export class Player extends Character implements CurrentGamerInterface { this.getBody().setImmovable(false); if (typeof companion === 'string') { - this.addCompanion(companion); + this.addCompanion(companion, companionTexturePromise); } } From fb90111cdf30c77740f0d027a34dc9558af76e55 Mon Sep 17 00:00:00 2001 From: Johannes Berthel Date: Tue, 6 Apr 2021 20:12:10 +0200 Subject: [PATCH 19/66] refactored companion selection scene --- .../src/Phaser/Login/SelectCompanionScene.ts | 154 ++++++++---------- 1 file changed, 69 insertions(+), 85 deletions(-) diff --git a/front/src/Phaser/Login/SelectCompanionScene.ts b/front/src/Phaser/Login/SelectCompanionScene.ts index 3d20d35a..34892a3c 100644 --- a/front/src/Phaser/Login/SelectCompanionScene.ts +++ b/front/src/Phaser/Login/SelectCompanionScene.ts @@ -24,8 +24,6 @@ export class SelectCompanionScene extends ResizableScene { private readonly nbCharactersPerRow = 7; private selectedRectangle!: Rectangle; - private selectedRectangleXPos = 0; - private selectedRectangleYPos = 0; private selectedCompanion!: Phaser.Physics.Arcade.Sprite; private companions: Array = new Array(); @@ -70,76 +68,85 @@ export class SelectCompanionScene extends ResizableScene { this.add.existing(this.logo); // input events - this.input.keyboard.on('keyup-ENTER', () => { - return this.nextScene(); - }); + this.input.keyboard.on('keyup-ENTER', this.nextScene.bind(this)); - this.input.keyboard.on('keydown-RIGHT', () => { - if(this.selectedRectangleYPos * this.nbCharactersPerRow + (this.selectedRectangleXPos + 2)) - if ( - this.selectedRectangleXPos < this.nbCharactersPerRow - 1 - && ((this.selectedRectangleYPos * this.nbCharactersPerRow) + (this.selectedRectangleXPos + 1) + 1) <= this.companionModels.length - ) { - this.selectedRectangleXPos++; - } - this.updateSelectedCompanion(); - }); - - this.input.keyboard.on('keydown-LEFT', () => { - if ( - this.selectedRectangleXPos > 0 - && ((this.selectedRectangleYPos * this.nbCharactersPerRow) + (this.selectedRectangleXPos - 1) + 1) <= this.companionModels.length - ) { - this.selectedRectangleXPos--; - } - this.updateSelectedCompanion(); - }); - - this.input.keyboard.on('keydown-DOWN', () => { - if ( - this.selectedRectangleYPos + 1 < Math.ceil(this.companionModels.length / this.nbCharactersPerRow) - && ( - (((this.selectedRectangleYPos + 1) * this.nbCharactersPerRow) + this.selectedRectangleXPos + 1) <= this.companionModels.length // check if companion isn't empty - || (this.selectedRectangleYPos + 1) === Math.ceil(this.companionModels.length / this.nbCharactersPerRow) // check if is custom rectangle - ) - ) { - this.selectedRectangleYPos++; - } - this.updateSelectedCompanion(); - }); - - this.input.keyboard.on('keydown-UP', () => { - if ( - this.selectedRectangleYPos > 0 - && (((this.selectedRectangleYPos - 1) * this.nbCharactersPerRow) + this.selectedRectangleXPos + 1) <= this.companionModels.length - ) { - this.selectedRectangleYPos--; - } - this.updateSelectedCompanion(); - }); + this.input.keyboard.on('keydown-RIGHT', this.selectNext.bind(this)); + this.input.keyboard.on('keydown-LEFT', this.selectPrevious.bind(this)); + this.input.keyboard.on('keydown-DOWN', this.jumpToNextRow.bind(this)); + this.input.keyboard.on('keydown-UP', this.jumpToPreviousRow.bind(this)); this.createCurrentCompanion(); - - const companionNumber = this.getCompanionIndex(); - - this.selectedRectangleXPos = companionNumber % this.nbCharactersPerRow; - this.selectedRectangleYPos = Math.floor(companionNumber / this.nbCharactersPerRow); - - this.updateSelectedCompanion(); + this.selectCompanion(this.getCompanionIndex()); } update(time: number, delta: number): void { this.pressReturnField.setVisible(!!(Math.floor(time / 500) % 2)); } - private nextScene(): void { - // store companion - const companionNumber = this.selectedRectangleXPos + this.selectedRectangleYPos * this.nbCharactersPerRow; - const model = this.companionModels[companionNumber]; + private jumpToPreviousRow(): void { + const index = this.companions.indexOf(this.selectedCompanion) - this.nbCharactersPerRow; + if (index >= 0) { + this.selectCompanion(index); + } + } + + private jumpToNextRow(): void { + const index = this.companions.indexOf(this.selectedCompanion) + this.nbCharactersPerRow; + if (index < this.companions.length) { + this.selectCompanion(index); + } + } + + private selectPrevious(): void { + const index = this.companions.indexOf(this.selectedCompanion); + this.selectCompanion(index - 1); + } + + private selectNext(): void { + const index = this.companions.indexOf(this.selectedCompanion); + this.selectCompanion(index + 1); + } + + private selectCompanion(index?: number): void { + if (typeof index === 'undefined') { + index = this.companions.indexOf(this.selectedCompanion); + } + + // make sure index is inside possible range + index = Math.min(this.companions.length - 1, Math.max(0, index)); + + if (this.selectedCompanion === this.companions[index]) { + return; + } + + this.selectedCompanion.anims.pause(); + this.selectedCompanion = this.companions[index]; + + this.selectedRectangle.setVisible(true); + this.selectedRectangle.setX(this.selectedCompanion.x); + this.selectedRectangle.setY(this.selectedCompanion.y); + this.selectedRectangle.setSize(32, 32); + + const model = this.companionModels[index]; + + if (model !== null) { + this.selectedCompanion.anims.play(model.name); + } + } + + private storeCompanionSelection(): string|null { + const index = this.companions.indexOf(this.selectedCompanion); + const model = this.companionModels[index]; const companion = model === null ? null : model.name; localUserStore.setCompanion(companion); + return companion; + } + + private nextScene(): void { + const companion = this.storeCompanionSelection(); + // next scene this.scene.stop(SelectCompanionSceneName); @@ -177,9 +184,7 @@ export class SelectCompanionScene extends ResizableScene { } companion.setInteractive().on("pointerdown", () => { - this.selectedRectangleXPos = col; - this.selectedRectangleYPos = row; - this.updateSelectedCompanion(); + this.selectCompanion(i); }); this.companions.push(companion); @@ -195,34 +200,13 @@ export class SelectCompanionScene extends ResizableScene { ]; } - private updateSelectedCompanion(): void { - this.selectedCompanion?.anims.pause(); - - const [x, y] = this.getCharacterPosition(this.selectedRectangleXPos, this.selectedRectangleYPos); - this.selectedRectangle.setVisible(true); - this.selectedRectangle.setX(x); - this.selectedRectangle.setY(y); - this.selectedRectangle.setSize(32, 32); - - const companionNumber = this.selectedRectangleXPos + this.selectedRectangleYPos * this.nbCharactersPerRow; - const model = this.companionModels[companionNumber]; - - const companion = this.companions[companionNumber]; - - if (model !== null) { - companion.play(model.name); - } - - this.selectedCompanion = companion; - } - public onResize(ev: UIEvent): void { this.textField.x = this.game.renderer.width / 2; this.pressReturnField.x = this.game.renderer.width / 2; this.logo.x = this.game.renderer.width - 30; this.logo.y = this.game.renderer.height - 20; - for (let i = 0; i Date: Tue, 6 Apr 2021 20:31:08 +0200 Subject: [PATCH 20/66] fixed rectangle resize issue --- front/src/Phaser/Login/SelectCompanionScene.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/front/src/Phaser/Login/SelectCompanionScene.ts b/front/src/Phaser/Login/SelectCompanionScene.ts index 34892a3c..9b5c38fb 100644 --- a/front/src/Phaser/Login/SelectCompanionScene.ts +++ b/front/src/Phaser/Login/SelectCompanionScene.ts @@ -122,10 +122,7 @@ export class SelectCompanionScene extends ResizableScene { this.selectedCompanion.anims.pause(); this.selectedCompanion = this.companions[index]; - this.selectedRectangle.setVisible(true); - this.selectedRectangle.setX(this.selectedCompanion.x); - this.selectedRectangle.setY(this.selectedCompanion.y); - this.selectedRectangle.setSize(32, 32); + this.redrawSelectedRectangle(); const model = this.companionModels[index]; @@ -134,6 +131,13 @@ export class SelectCompanionScene extends ResizableScene { } } + private redrawSelectedRectangle(): void { + this.selectedRectangle.setVisible(true); + this.selectedRectangle.setX(this.selectedCompanion.x); + this.selectedRectangle.setY(this.selectedCompanion.y); + this.selectedRectangle.setSize(32, 32); + } + private storeCompanionSelection(): string|null { const index = this.companions.indexOf(this.selectedCompanion); const model = this.companionModels[index]; @@ -217,7 +221,7 @@ export class SelectCompanionScene extends ResizableScene { companion.y = y; } - this.selectCompanion(); + this.redrawSelectedRectangle(); } private getCompanionIndex(): number { From 73e515c2368fcb6667a8f94b12d93231a377b646 Mon Sep 17 00:00:00 2001 From: Johannes Berthel Date: Thu, 8 Apr 2021 18:42:05 +0200 Subject: [PATCH 21/66] don't show companion scene after first refresh --- front/src/Phaser/Game/GameManager.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts index 70625b24..41d827a3 100644 --- a/front/src/Phaser/Game/GameManager.ts +++ b/front/src/Phaser/Game/GameManager.ts @@ -5,7 +5,6 @@ import {MenuScene, MenuSceneName} from "../Menu/MenuScene"; import {HelpCameraSettingsScene, HelpCameraSettingsSceneName} from "../Menu/HelpCameraSettingsScene"; import {LoginSceneName} from "../Login/LoginScene"; import {SelectCharacterSceneName} from "../Login/SelectCharacterScene"; -import {SelectCompanionSceneName} from "../Login/SelectCompanionScene"; import {EnableCameraSceneName} from "../Login/EnableCameraScene"; import {localUserStore} from "../../Connexion/LocalUserStore"; @@ -40,8 +39,6 @@ export class GameManager { return LoginSceneName; } else if (!this.characterLayers) { return SelectCharacterSceneName; - } else if (!localUserStore.wasCompanionSet()) { - return SelectCompanionSceneName; } else { return EnableCameraSceneName; } From 8d4e0c3e88db92c142c2f424b93a46fe3a02a463 Mon Sep 17 00:00:00 2001 From: Johannes Berthel Date: Fri, 9 Apr 2021 18:30:30 +0200 Subject: [PATCH 22/66] added companion compatibility with phaser 3.54.0 --- .../Companion/CompanionTexturesLoadingManager.ts | 2 ++ front/src/Phaser/Entity/Character.ts | 4 ---- front/src/Phaser/Game/GameScene.ts | 10 ++++++++++ 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts b/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts index 354c5740..75c20a48 100644 --- a/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts +++ b/front/src/Phaser/Companion/CompanionTexturesLoadingManager.ts @@ -23,5 +23,7 @@ export const lazyLoadCompanionResource = (loader: LoaderPlugin, name: string): P loader.spritesheet(resource.name, resource.img, { frameWidth: 32, frameHeight: 32, endFrame: 12 }); loader.once(`filecomplete-spritesheet-${resource.name}`, () => resolve(resource.name)); + + loader.start(); // It's only automatically started during the Scene preload. }); } diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index 5b541267..9f2bd1fd 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -227,9 +227,5 @@ export abstract class Character extends Container { } super.destroy(); this.playerName.destroy(); - - if (this.companion) { - this.companion.destroy(); - } } } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 7cbefb39..5feb0607 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -854,6 +854,11 @@ ${escapedMessage} private removeAllRemotePlayers(): void { this.MapPlayersByKey.forEach((player: RemotePlayer) => { player.destroy(); + + if (player.companion) { + player.companion.destroy(); + } + this.MapPlayers.remove(player); }); this.MapPlayersByKey = new Map(); @@ -1243,6 +1248,11 @@ ${escapedMessage} console.error('Cannot find user with id ', userId); } else { player.destroy(); + + if (player.companion) { + player.companion.destroy(); + } + this.MapPlayers.remove(player); } this.MapPlayersByKey.delete(userId); From 8e467f3e10c8cd2546bb63916a3fdf1269154e2b Mon Sep 17 00:00:00 2001 From: kharhamel Date: Wed, 7 Apr 2021 14:09:45 +0200 Subject: [PATCH 23/66] FIX: improved the validation from localstorage for username and characterLayers --- front/src/Connexion/LocalUser.ts | 19 ++++++++ front/src/Connexion/LocalUserStore.ts | 10 +++-- front/src/Enum/EnvironmentVariable.ts | 1 + front/src/Phaser/Game/GameManager.ts | 2 +- front/src/Phaser/Login/CustomizeScene.ts | 4 ++ front/src/Phaser/Login/LoginScene.ts | 8 ++-- .../src/Phaser/Login/SelectCharacterScene.ts | 4 ++ front/tests/Phaser/Connexion/LocalUserTest.ts | 45 +++++++++++++++++++ 8 files changed, 84 insertions(+), 9 deletions(-) create mode 100644 front/tests/Phaser/Connexion/LocalUserTest.ts diff --git a/front/src/Connexion/LocalUser.ts b/front/src/Connexion/LocalUser.ts index 06d98b70..b8e0a204 100644 --- a/front/src/Connexion/LocalUser.ts +++ b/front/src/Connexion/LocalUser.ts @@ -1,3 +1,5 @@ +import {MAX_USERNAME_LENGTH} from "../Enum/EnvironmentVariable"; + export interface CharacterTexture { id: number, level: number, @@ -5,6 +7,23 @@ export interface CharacterTexture { rights: string } +export const maxUserNameLength: number = MAX_USERNAME_LENGTH; + +export function isUserNameValid(value: string): boolean { + const regexp = new RegExp('^[A-Za-z]{1,'+maxUserNameLength+'}$'); + return regexp.test(value); +} + +export function areCharacterLayersValid(value: string[]): boolean { + if (!value.length) return false; + for (let i = 0; i < value.length; i++) { + if (/^\w+$/.exec(value[i]) === null) { + return false; + } + } + return true; +} + export class LocalUser { constructor(public readonly uuid:string, public readonly jwtToken: string, public readonly textures: CharacterTexture[]) { } diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts index 702df561..48b5697b 100644 --- a/front/src/Connexion/LocalUserStore.ts +++ b/front/src/Connexion/LocalUserStore.ts @@ -1,4 +1,4 @@ -import {LocalUser} from "./LocalUser"; +import {areCharacterLayersValid, isUserNameValid, LocalUser} from "./LocalUser"; const playerNameKey = 'playerName'; const selectedPlayerKey = 'selectedPlayer'; @@ -22,8 +22,9 @@ class LocalUserStore { setName(name:string): void { localStorage.setItem(playerNameKey, name); } - getName(): string { - return localStorage.getItem(playerNameKey) || ''; + getName(): string|null { + const value = localStorage.getItem(playerNameKey) || ''; + return isUserNameValid(value) ? value : null; } setPlayerCharacterIndex(playerCharacterIndex: number): void { @@ -44,7 +45,8 @@ class LocalUserStore { localStorage.setItem(characterLayersKey, JSON.stringify(layers)); } getCharacterLayers(): string[]|null { - return JSON.parse(localStorage.getItem(characterLayersKey) || "null"); + const value = JSON.parse(localStorage.getItem(characterLayersKey) || "null"); + return areCharacterLayersValid(value) ? value : null; } setGameQualityValue(value: number): void { diff --git a/front/src/Enum/EnvironmentVariable.ts b/front/src/Enum/EnvironmentVariable.ts index ea2434af..5040e59f 100644 --- a/front/src/Enum/EnvironmentVariable.ts +++ b/front/src/Enum/EnvironmentVariable.ts @@ -14,6 +14,7 @@ const RESOLUTION = 2; const ZOOM_LEVEL = 1/*3/4*/; const POSITION_DELAY = 200; // Wait 200ms between sending position events const MAX_EXTRAPOLATION_TIME = 100; // Extrapolate a maximum of 250ms if no new movement is sent by the player +export const MAX_USERNAME_LENGTH = parseInt(process.env.MAX_USERNAME_LENGTH || '') || 8; export { DEBUG_MODE, diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts index 8751796f..e5ed7bba 100644 --- a/front/src/Phaser/Game/GameManager.ts +++ b/front/src/Phaser/Game/GameManager.ts @@ -35,7 +35,7 @@ export class GameManager { if (!this.playerName) { return LoginSceneName; - } else if (!this.characterLayers) { + } else if (!this.characterLayers || !this.characterLayers.length) { return SelectCharacterSceneName; } else { return EnableCameraSceneName; diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 4f5b2860..6cf13a86 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -11,6 +11,7 @@ import {localUserStore} from "../../Connexion/LocalUserStore"; import {addLoader} from "../Components/Loader"; import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures"; import {AbstractCharacterScene} from "./AbstractCharacterScene"; +import {areCharacterLayersValid} from "../../Connexion/LocalUser"; export const CustomizeSceneName = "CustomizeScene"; @@ -111,6 +112,9 @@ export class CustomizeScene extends AbstractCharacterScene { } i++; } + if (!areCharacterLayersValid(layers)) { + return; + } gameManager.setCharacterLayers(layers); diff --git a/front/src/Phaser/Login/LoginScene.ts b/front/src/Phaser/Login/LoginScene.ts index 9ca6dcd2..ffdf1367 100644 --- a/front/src/Phaser/Login/LoginScene.ts +++ b/front/src/Phaser/Login/LoginScene.ts @@ -4,6 +4,7 @@ import {TextInput} from "../Components/TextInput"; import Image = Phaser.GameObjects.Image; import {SelectCharacterSceneName} from "./SelectCharacterScene"; import {ResizableScene} from "./ResizableScene"; +import {isUserNameValid, maxUserNameLength} from "../../Connexion/LocalUser"; //todo: put this constants in a dedicated file export const LoginSceneName = "LoginScene"; @@ -37,7 +38,7 @@ export class LoginScene extends ResizableScene { create() { this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Enter your name:'); - this.nameInput = new TextInput(this, this.game.renderer.width / 2, 70, 8, this.name,(text: string) => { + this.nameInput = new TextInput(this, this.game.renderer.width / 2, 70, maxUserNameLength, this.name,(text: string) => { this.name = text; }); @@ -50,10 +51,9 @@ export class LoginScene extends ResizableScene { this.infoTextField = new TextField(this, 10, this.game.renderer.height - 35, infoText, false); this.input.keyboard.on('keyup-ENTER', () => { - if (this.name === '') { - return + if (isUserNameValid(this.name)) { + this.login(this.name); } - this.login(this.name); }); } diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index e47cf38a..a1f3fd81 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -10,6 +10,7 @@ import {loadAllDefaultModels, loadCustomTexture} from "../Entity/PlayerTexturesL import {addLoader} from "../Components/Loader"; import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures"; import {AbstractCharacterScene} from "./AbstractCharacterScene"; +import {areCharacterLayersValid} from "../../Connexion/LocalUser"; //todo: put this constants in a dedicated file @@ -142,6 +143,9 @@ export class SelectCharacterScene extends AbstractCharacterScene { } private nextScene(): void { + if (this.selectedPlayer !== null && !areCharacterLayersValid([this.selectedPlayer.texture.key])) { + return; + } this.scene.stop(SelectCharacterSceneName); if (this.selectedPlayer !== null) { gameManager.setCharacterLayers([this.selectedPlayer.texture.key]); diff --git a/front/tests/Phaser/Connexion/LocalUserTest.ts b/front/tests/Phaser/Connexion/LocalUserTest.ts new file mode 100644 index 00000000..00d8f400 --- /dev/null +++ b/front/tests/Phaser/Connexion/LocalUserTest.ts @@ -0,0 +1,45 @@ +import "jasmine"; +import {areCharacterLayersValid, isUserNameValid, maxUserNameLength} from "../../../src/Connexion/LocalUser"; + +describe("isUserNameValid()", () => { + it("should validate name with letters", () => { + expect(isUserNameValid('toto')).toBe(true); + }); + + it("should not validate empty name", () => { + expect(isUserNameValid('')).toBe(false); + }); + it("should not validate string with too many letters", () => { + let testString = ''; + for (let i = 0; i < maxUserNameLength + 2; i++) { + testString += 'a'; + } + expect(isUserNameValid(testString)).toBe(false); + }); + it("should not validate spaces", () => { + expect(isUserNameValid(' ')).toBe(false); + }); + it("should not validate numbers", () => { + expect(isUserNameValid('a12')).toBe(false); + }); + it("should not validate special characters", () => { + expect(isUserNameValid('a&-')).toBe(false); + }); +}); + +describe("areCharacterLayersValid()", () => { + it("should validate default textures array", () => { + expect(areCharacterLayersValid(['male1', 'male2'])).toBe(true); + }); + + it("should not validate an empty array", () => { + expect(areCharacterLayersValid([])).toBe(false); + }); + it("should not validate space only strings", () => { + expect(areCharacterLayersValid([' ', 'male1'])).toBe(false); + }); + + it("should not validate empty strings", () => { + expect(areCharacterLayersValid(['', 'male1'])).toBe(false); + }); +}); \ No newline at end of file From c7dcaec9408bfcba7b07423921fc36b022005dbc Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Wed, 3 Feb 2021 23:11:19 +0100 Subject: [PATCH 24/66] Make login text input field touch capable (thx @TabscoEye) --- front/src/Phaser/Components/TextInput.ts | 71 +++++++++++++++++------- 1 file changed, 51 insertions(+), 20 deletions(-) diff --git a/front/src/Phaser/Components/TextInput.ts b/front/src/Phaser/Components/TextInput.ts index 1e01029b..a8ea772f 100644 --- a/front/src/Phaser/Components/TextInput.ts +++ b/front/src/Phaser/Components/TextInput.ts @@ -1,46 +1,68 @@ +const IGNORED_KEYS = new Set([ + 'Esc', + 'Escape', + 'Alt', + 'Meta', + 'Control', + 'Ctrl', + 'Space', + 'Backspace' +]) + export class TextInput extends Phaser.GameObjects.BitmapText { private minUnderLineLength = 4; private underLine: Phaser.GameObjects.Text; + private domInput = document.createElement('input'); - constructor(scene: Phaser.Scene, x: number, y: number, maxLength: number, text: string, onChange: (text: string) => void) { + constructor(scene: Phaser.Scene, x: number, y: number, maxLength: number, text: string, + onChange: (text: string) => void) { super(scene, x, y, 'main_font', text, 32); - this.setOrigin(0.5).setCenterAlign() + this.setOrigin(0.5).setCenterAlign(); this.scene.add.existing(this); - this.underLine = this.scene.add.text(x, y+1, this.getUnderLineBody(text.length), { fontFamily: 'Arial', fontSize: "32px", color: '#ffffff'}) - this.underLine.setOrigin(0.5) + const style = {fontFamily: 'Arial', fontSize: "32px", color: '#ffffff'}; + this.underLine = this.scene.add.text(x, y+1, this.getUnderLineBody(text.length), style); + this.underLine.setOrigin(0.5); + this.domInput.maxLength = maxLength; + this.domInput.style.opacity = "0"; + if (text) { + this.domInput.value = text; + } - this.scene.input.keyboard.on('keydown', (event: KeyboardEvent) => { - if (event.keyCode === 8 && this.text.length > 0) { - this.deleteLetter(); - } else if ((event.keyCode === 32 || (event.keyCode >= 48 && event.keyCode <= 90)) && this.text.length < maxLength) { - this.addLetter(event.key); + this.domInput.addEventListener('keydown', event => { + if (IGNORED_KEYS.has(event.key)) { + return; } + + if (!/[a-zA-Z0-9:.!&?()+-]/.exec(event.key)) { + event.preventDefault(); + } + }); + + this.domInput.addEventListener('input', (event) => { + if (event.defaultPrevented) { + return; + } + this.text = this.domInput.value; this.underLine.text = this.getUnderLineBody(this.text.length); onChange(this.text); }); + + document.body.append(this.domInput); + this.focus(); } - + private getUnderLineBody(textLength:number): string { if (textLength < this.minUnderLineLength) textLength = this.minUnderLineLength; let text = '_______'; for (let i = this.minUnderLineLength; i < textLength; i++) { - text += '__' + text += '__'; } return text; } - private deleteLetter() { - this.text = this.text.substr(0, this.text.length - 1); - } - - - private addLetter(letter: string) { - this.text += letter; - } - getText(): string { return this.text; } @@ -56,4 +78,13 @@ export class TextInput extends Phaser.GameObjects.BitmapText { this.underLine.y = y+1; return this; } + + focus() { + this.domInput.focus(); + } + + destroy(): void { + super.destroy(); + this.domInput.remove(); + } } From e80735027919fa362418485fda1f61de7fa7c174 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Wed, 3 Feb 2021 23:22:23 +0100 Subject: [PATCH 25/66] Add touch input support to login scenes (thx @TabscoEye) --- front/src/Phaser/Login/EnableCameraScene.ts | 17 +++++++++- front/src/Phaser/Login/LoginScene.ts | 32 ++++++++++++++++--- .../src/Phaser/Login/SelectCharacterScene.ts | 17 ++++++++-- 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/front/src/Phaser/Login/EnableCameraScene.ts b/front/src/Phaser/Login/EnableCameraScene.ts index a3ca5cf1..c3aa5077 100644 --- a/front/src/Phaser/Login/EnableCameraScene.ts +++ b/front/src/Phaser/Login/EnableCameraScene.ts @@ -1,6 +1,7 @@ import {gameManager} from "../Game/GameManager"; import {TextField} from "../Components/TextField"; import Image = Phaser.GameObjects.Image; +import Rectangle = Phaser.GameObjects.Rectangle; import {mediaManager} from "../../WebRtc/MediaManager"; import {RESOLUTION} from "../../Enum/EnvironmentVariable"; import {SoundMeter} from "../Components/SoundMeter"; @@ -35,6 +36,7 @@ export class EnableCameraScene extends Phaser.Scene { private microphoneNameField!: TextField; private repositionCallback!: (this: Window, ev: UIEvent) => void; + private mobileTapRectangle!: Rectangle; constructor() { super({ key: EnableCameraSceneName @@ -54,7 +56,19 @@ export class EnableCameraScene extends Phaser.Scene { create() { this.textField = new TextField(this, this.game.renderer.width / 2, 20, 'Turn on your camera and microphone'); - this.pressReturnField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height - 30, 'Press enter to start'); + this.pressReturnField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height - 30, 'Touch here\n\n or \n\nPress enter to start'); + // For mobile purposes - we need a big enough touchable area. + this.mobileTapRectangle = this.add + .rectangle( + this.game.renderer.width / 2, + this.game.renderer.height - 30, + 200, + 50, + ) + .setInteractive() + .on("pointerdown", () => { + this.login(); + }); this.cameraNameField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height - 60, ''); @@ -195,6 +209,7 @@ export class EnableCameraScene extends Phaser.Scene { } this.textField.x = this.game.renderer.width / 2; + this.mobileTapRectangle.x = this.game.renderer.width / 2; this.cameraNameField.x = this.game.renderer.width / 2; this.microphoneNameField.x = this.game.renderer.width / 2; this.pressReturnField.x = this.game.renderer.width / 2; diff --git a/front/src/Phaser/Login/LoginScene.ts b/front/src/Phaser/Login/LoginScene.ts index ffdf1367..4ff582b4 100644 --- a/front/src/Phaser/Login/LoginScene.ts +++ b/front/src/Phaser/Login/LoginScene.ts @@ -5,6 +5,8 @@ import Image = Phaser.GameObjects.Image; import {SelectCharacterSceneName} from "./SelectCharacterScene"; import {ResizableScene} from "./ResizableScene"; import {isUserNameValid, maxUserNameLength} from "../../Connexion/LocalUser"; +import { localUserStore } from "../../Connexion/LocalUserStore"; +import Rectangle = Phaser.GameObjects.Rectangle; //todo: put this constants in a dedicated file export const LoginSceneName = "LoginScene"; @@ -20,6 +22,7 @@ export class LoginScene extends ResizableScene { private pressReturnField!: TextField; private logo!: Image; private name: string = ''; + private mobileTapRectangle!: Rectangle; constructor() { super({ @@ -37,17 +40,36 @@ export class LoginScene extends ResizableScene { create() { - this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Enter your name:'); this.nameInput = new TextInput(this, this.game.renderer.width / 2, 70, maxUserNameLength, this.name,(text: string) => { this.name = text; - }); + localUserStore.setName(text); + }) + .setInteractive() + .on('pointerdown', () => { + this.nameInput.focus(); + }) - this.pressReturnField = new TextField(this, this.game.renderer.width / 2, 130, 'Press enter to start'); + this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Enter your name:') + .setInteractive() + .on('pointerdown', () => { + this.nameInput.focus(); + }) + // For mobile purposes - we need a big enough touchable area. + this.mobileTapRectangle = this.add.rectangle( + this.game.renderer.width / 2, + 130, + this.game.renderer.width / 2, + 60, + ).setInteractive() + .on('pointerdown', () => { + this.login(this.name) + }) + this.pressReturnField = new TextField(this, this.game.renderer.width / 2, 130, 'Touch here\n\n or \n\nPress enter to start') this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, LoginTextures.icon); this.add.existing(this.logo); - const infoText = "Commands: \n - Arrows or Z,Q,S,D to move\n - SHIFT to run"; + const infoText = "Commands: \n - Arrows or W, A, S, D to move\n - SHIFT to run"; this.infoTextField = new TextField(this, 10, this.game.renderer.height - 35, infoText, false); this.input.keyboard.on('keyup-ENTER', () => { @@ -66,6 +88,7 @@ export class LoginScene extends ResizableScene { } private login(name: string): void { + if (this.name === '') return gameManager.setPlayerName(name); this.scene.stop(LoginSceneName) @@ -77,6 +100,7 @@ export class LoginScene extends ResizableScene { this.textField.x = this.game.renderer.width / 2; this.nameInput.setX(this.game.renderer.width / 2 - 64); this.pressReturnField.x = this.game.renderer.width / 2; + this.mobileTapRectangle.x = this.game.renderer.width / 2; this.logo.x = this.game.renderer.width - 30; this.logo.y = this.game.renderer.height - 20; this.infoTextField.y = this.game.renderer.height - 35; diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index a1f3fd81..821fff20 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -36,6 +36,7 @@ export class SelectCharacterScene extends AbstractCharacterScene { private selectedRectangleYPos = 0; // Number of the character selected in the columns private selectedPlayer!: Phaser.Physics.Arcade.Sprite|null; // null if we are selecting the "customize" option private players: Array = new Array(); + private mobileTapRectangle!: Rectangle; private playerModels!: BodyResourceDescriptionInterface[]; constructor() { @@ -69,8 +70,20 @@ export class SelectCharacterScene extends AbstractCharacterScene { this.pressReturnField = new TextField( this, this.game.renderer.width / 2, - 90 + 32 * Math.ceil( this.playerModels.length / this.nbCharactersPerRow) + 40, - 'Press enter to start'); + 90 + 32 * Math.ceil( this.playerModels.length / this.nbCharactersPerRow) + 60, + 'Touch here\n\n or \n\nPress enter to start'); + // For mobile purposes - we need a big enough touchable area. + this.mobileTapRectangle = this.add + .rectangle( + this.game.renderer.width / 2, + 275, + this.game.renderer.width / 2, + 50, + ) + .setInteractive() + .on("pointerdown", () => { + this.nextScene(); + }); const rectangleXStart = this.game.renderer.width / 2 - (this.nbCharactersPerRow / 2) * 32 + 16; From aab65298aad3c92dda62f5a931c8f44f9178390f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C2=AF=5C=5F=28=E3=83=84=29=5F/=C2=AF?= Date: Sat, 20 Feb 2021 20:22:43 +0100 Subject: [PATCH 26/66] Add touch support to character customization screen --- front/src/Phaser/Login/CustomizeScene.ts | 89 ++++++++++++++++++- .../src/Phaser/Login/SelectCharacterScene.ts | 4 + 2 files changed, 91 insertions(+), 2 deletions(-) diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts index 6cf13a86..3b27f01e 100644 --- a/front/src/Phaser/Login/CustomizeScene.ts +++ b/front/src/Phaser/Login/CustomizeScene.ts @@ -35,6 +35,13 @@ export class CustomizeScene extends AbstractCharacterScene { private Rectangle!: Rectangle; + private mobileTapUP!: Rectangle; + private mobileTapDOWN!: Rectangle; + private mobileTapLEFT!: Rectangle; + private mobileTapRIGHT!: Rectangle; + + private mobileTapENTER!: Rectangle; + private logo!: Image; private selectedLayers: number[] = [0]; @@ -70,7 +77,7 @@ export class CustomizeScene extends AbstractCharacterScene { create() { this.textField = new TextField(this, this.game.renderer.width / 2, 30, 'Customize your own Avatar!'); - this.enterField = new TextField(this, this.game.renderer.width / 2, 40, 'you can start the game by pressing SPACE..'); + this.enterField = new TextField(this, this.game.renderer.width / 2, 60, 'Start the game by pressing ENTER\n\n or touching the center rectangle'); this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, CustomizeTextures.icon); this.add.existing(this.logo); @@ -78,22 +85,88 @@ export class CustomizeScene extends AbstractCharacterScene { this.arrowRight = new Image(this, this.game.renderer.width*0.9, this.game.renderer.height/2, CustomizeTextures.arrowRight); this.add.existing(this.arrowRight); + this.mobileTapRIGHT = this.add + .rectangle( + this.game.renderer.width*0.9, + this.game.renderer.height/2, + 32, + 32, + ) + .setInteractive() + .on("pointerdown", () => { + this.moveCursorHorizontally(1); + }); this.arrowLeft = new Image(this, this.game.renderer.width/9, this.game.renderer.height/2, CustomizeTextures.arrowRight); this.arrowLeft.flipX = true; this.add.existing(this.arrowLeft); - + this.mobileTapLEFT = this.add + .rectangle( + this.game.renderer.width/9, + this.game.renderer.height/2, + 32, + 32, + ) + .setInteractive() + .on("pointerdown", () => { + this.moveCursorHorizontally(-1); + }); this.Rectangle = this.add.rectangle(this.cameras.main.worldView.x + this.cameras.main.width / 2, this.cameras.main.worldView.y + this.cameras.main.height / 2, 32, 33) this.Rectangle.setStrokeStyle(2, 0xFFFFFF); this.add.existing(this.Rectangle); + this.mobileTapENTER = this.add + .rectangle( + this.cameras.main.worldView.x + this.cameras.main.width / 2, + this.cameras.main.worldView.y + this.cameras.main.height / 2, + 32, + 32, + ) + .setInteractive() + .on("pointerdown", () => { + const layers: string[] = []; + let i = 0; + for (const layerItem of this.selectedLayers) { + if (layerItem !== undefined) { + layers.push(this.layers[i][layerItem].name); + } + i++; + } + + gameManager.setCharacterLayers(layers); + + this.scene.sleep(CustomizeSceneName); + gameManager.tryResumingGame(this, EnableCameraSceneName); + }); this.arrowDown = new Image(this, this.game.renderer.width - 30, 100, CustomizeTextures.arrowUp); this.arrowDown.flipY = true; this.add.existing(this.arrowDown); + this.mobileTapDOWN = this.add + .rectangle( + this.game.renderer.width - 30, + 100, + 32, + 32, + ) + .setInteractive() + .on("pointerdown", () => { + this.moveCursorVertically(1); + }); this.arrowUp = new Image(this, this.game.renderer.width - 30, 60, CustomizeTextures.arrowUp); this.add.existing(this.arrowUp); + this.mobileTapUP = this.add + .rectangle( + this.game.renderer.width - 30, + 60, + 32, + 32, + ) + .setInteractive() + .on("pointerdown", () => { + this.moveCursorVertically(-1); + }); this.createCustomizeLayer(0, 0, 0); this.createCustomizeLayer(0, 0, 1); @@ -268,7 +341,9 @@ export class CustomizeScene extends AbstractCharacterScene { this.moveLayers(); this.Rectangle.x = this.cameras.main.worldView.x + this.cameras.main.width / 2; + this.mobileTapENTER.x = this.cameras.main.worldView.x + this.cameras.main.width / 2; this.Rectangle.y = this.cameras.main.worldView.y + this.cameras.main.height / 2; + this.mobileTapENTER.y = this.cameras.main.worldView.y + this.cameras.main.height / 2; this.textField.x = this.game.renderer.width/2; @@ -276,15 +351,25 @@ export class CustomizeScene extends AbstractCharacterScene { this.logo.y = this.game.renderer.height - 20; this.arrowUp.x = this.game.renderer.width - 30; + this.mobileTapUP.x = this.game.renderer.width - 30; this.arrowUp.y = 60; + this.mobileTapUP.y = 60; this.arrowDown.x = this.game.renderer.width - 30; + this.mobileTapDOWN.x = this.game.renderer.width - 30; this.arrowDown.y = 100; + this.mobileTapDOWN.y = 100; this.arrowLeft.x = this.game.renderer.width/9; + this.mobileTapLEFT.x = this.game.renderer.width/9; this.arrowLeft.y = this.game.renderer.height/2; + this.mobileTapLEFT.y = this.game.renderer.height/2; this.arrowRight.x = this.game.renderer.width*0.9; + this.mobileTapRIGHT.x = this.game.renderer.width*0.9; this.arrowRight.y = this.game.renderer.height/2; + this.mobileTapRIGHT.y = this.game.renderer.height/2; + + } } diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index 821fff20..edd09a91 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -206,6 +206,10 @@ export class SelectCharacterScene extends AbstractCharacterScene { this.customizeButton.setInteractive().on("pointerdown", () => { this.selectedRectangleYPos = Math.ceil(this.playerModels.length / this.nbCharactersPerRow); this.updateSelectedPlayer(); + this.nextScene(); + }); + this.customizeButtonSelected.setInteractive().on("pointerdown", () => { + this.nextScene(); }); this.selectedPlayer = this.players[0]; From edd4e9e95d7fcf223c43fe616fb53fca33a304e5 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Tue, 9 Mar 2021 20:01:48 +0100 Subject: [PATCH 27/66] Add touch support for Jitsi and website triggers --- front/src/Phaser/Game/GameScene.ts | 4 ++-- front/src/WebRtc/LayoutManager.ts | 10 +++------- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index a9ba86a6..24511582 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -668,7 +668,7 @@ export class GameScene extends ResizableScene implements CenterListener { if(openWebsiteTriggerValue && openWebsiteTriggerValue === ON_ACTION_TRIGGER_BUTTON) { let message = allProps.get(WEBSITE_MESSAGE_PROPERTIES); if(message === undefined){ - message = 'Press on SPACE to open the web site'; + message = 'Press SPACE or touch here to open web site'; } layoutManager.addActionButton('openWebsite', message.toString(), () => { openWebsiteFunction(); @@ -700,7 +700,7 @@ export class GameScene extends ResizableScene implements CenterListener { if(jitsiTriggerValue && jitsiTriggerValue === ON_ACTION_TRIGGER_BUTTON) { let message = allProps.get(JITSI_MESSAGE_PROPERTIES); if (message === undefined) { - message = 'Press on SPACE to enter in jitsi meet room'; + message = 'Press SPACE or touch here to enter Jitsi Meet room'; } layoutManager.addActionButton('jitsiRoom', message.toString(), () => { openJitsiRoomFunction(); diff --git a/front/src/WebRtc/LayoutManager.ts b/front/src/WebRtc/LayoutManager.ts index 233b5327..a0b805d4 100644 --- a/front/src/WebRtc/LayoutManager.ts +++ b/front/src/WebRtc/LayoutManager.ts @@ -340,14 +340,10 @@ class LayoutManager { const mainContainer = HtmlUtils.getElementByIdOrFail('main-container'); mainContainer.appendChild(div); - const callBackFunctionTrigger = (() => { - console.log('user click on space => ', id); - callBack(); - }); - //add trigger action - this.actionButtonTrigger.set(id, callBackFunctionTrigger); - userInputManager.addSpaceEventListner(callBackFunctionTrigger); + div.onpointerdown = () => callBack(); + this.actionButtonTrigger.set(id, callBack); + userInputManager.addSpaceEventListner(callBack); } public removeActionButton(id: string, userInputManager: UserInputManager){ From 5462af0dc786700a2a70e4ed199ca9905e0ad6ce Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Tue, 13 Apr 2021 01:41:26 +0200 Subject: [PATCH 28/66] Fix first login with empty local storage During first login on a new domain the local storage is empty. This led to a TypeError and hence blank black screen. --- front/src/Connexion/LocalUser.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/front/src/Connexion/LocalUser.ts b/front/src/Connexion/LocalUser.ts index b8e0a204..56483976 100644 --- a/front/src/Connexion/LocalUser.ts +++ b/front/src/Connexion/LocalUser.ts @@ -14,8 +14,8 @@ export function isUserNameValid(value: string): boolean { return regexp.test(value); } -export function areCharacterLayersValid(value: string[]): boolean { - if (!value.length) return false; +export function areCharacterLayersValid(value: string[] | null): boolean { + if (!value || !value.length) return false; for (let i = 0; i < value.length; i++) { if (/^\w+$/.exec(value[i]) === null) { return false; From e75fb9a6a9abc72468dc2545432ffb32d7d9baca Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Tue, 13 Apr 2021 00:44:51 +0200 Subject: [PATCH 29/66] Add touch support to companion selection scene --- front/src/Phaser/Login/SelectCompanionScene.ts | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/front/src/Phaser/Login/SelectCompanionScene.ts b/front/src/Phaser/Login/SelectCompanionScene.ts index 9b5c38fb..c7ee56f1 100644 --- a/front/src/Phaser/Login/SelectCompanionScene.ts +++ b/front/src/Phaser/Login/SelectCompanionScene.ts @@ -29,6 +29,8 @@ export class SelectCompanionScene extends ResizableScene { private companions: Array = new Array(); private companionModels: Array = [null]; + private confirmTouchArea!: Rectangle; + constructor() { super({ key: SelectCompanionSceneName @@ -54,12 +56,17 @@ export class SelectCompanionScene extends ResizableScene { create() { this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Select your companion'); + const confirmTouchAreaY = 115 + 32 * Math.ceil(this.companionModels.length / this.nbCharactersPerRow); this.pressReturnField = new TextField( this, this.game.renderer.width / 2, - 90 + 32 * Math.ceil(this.companionModels.length / this.nbCharactersPerRow), - 'Press enter to start' + confirmTouchAreaY, + 'Touch here\n\n or \n\n press enter to start' ); + this.confirmTouchArea = this.add + .rectangle(this.game.renderer.width / 2, confirmTouchAreaY, 200, 50) + .setInteractive() + .on("pointerdown", this.nextScene.bind(this)); const rectangleXStart = this.game.renderer.width / 2 - (this.nbCharactersPerRow / 2) * 32 + 16; this.selectedRectangle = this.add.rectangle(rectangleXStart, 90, 32, 32).setStrokeStyle(2, 0xFFFFFF); From c3230bc2b39d77f649e6843200daf2f79295dbeb Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Wed, 3 Feb 2021 23:28:46 +0100 Subject: [PATCH 30/66] Integrate virtual joystick --- front/dist/resources/html/gameMenu.html | 3 ++ front/dist/resources/style/style.css | 13 ++++++- front/package.json | 1 + front/src/Connexion/LocalUserStore.ts | 10 ++++- front/src/Phaser/Game/GameScene.ts | 27 ++++++++++++- front/src/Phaser/Menu/MenuScene.ts | 20 ++++++++++ .../src/Phaser/UserInput/UserInputManager.ts | 39 +++++++++++++++++-- front/src/types.ts | 22 +++++++++++ front/tsconfig.json | 21 +++++----- front/yarn.lock | 30 ++++++++++++++ 10 files changed, 170 insertions(+), 16 deletions(-) create mode 100644 front/src/types.ts diff --git a/front/dist/resources/html/gameMenu.html b/front/dist/resources/html/gameMenu.html index d5e9ad7e..fa46090a 100644 --- a/front/dist/resources/html/gameMenu.html +++ b/front/dist/resources/html/gameMenu.html @@ -36,6 +36,9 @@
+
+ +
diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index ba5b6c07..5544ca47 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -151,6 +151,7 @@ video#myCamVideo{ .btn-cam-action { + pointer-events: all; position: absolute; bottom: 0px; right: 0px; @@ -186,18 +187,26 @@ video#myCamVideo{ transition: 280ms; } .btn-micro{ + pointer-events: auto; transition: all .3s; right: 44px; } .btn-video{ + pointer-events: auto; transition: all .25s; right: 134px; } .btn-monitor{ + pointer-events: auto; transition: all .2s; right: 224px; } - +.btn-copy{ + pointer-events: auto; + transition: all .3s; + right: 44px; + opacity: 1; +} .btn-cam-action div img{ height: 22px; width: 30px; @@ -502,6 +511,7 @@ input[type=range]:focus::-ms-fill-upper { position: absolute; width: 100%; height: 100%; + pointer-events: none; /* TODO: DO WE NEED FLEX HERE???? WE WANT A SIDEBAR OF EXACTLY 25% (note: flex useful for direction!!!) */ } @@ -537,6 +547,7 @@ input[type=range]:focus::-ms-fill-upper { .sidebar { flex: 0 0 25%; display: flex; + pointer-events: none; } .sidebar > div { diff --git a/front/package.json b/front/package.json index 1ba7f3c2..6189ff4c 100644 --- a/front/package.json +++ b/front/package.json @@ -31,6 +31,7 @@ "generic-type-guard": "^3.2.0", "google-protobuf": "^3.13.0", "phaser": "3.24.1", + "phaser3-rex-plugins": "^1.1.42", "queue-typescript": "^1.0.1", "quill": "^1.3.7", "rxjs": "^6.6.3", diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts index 4741fc8c..11977f7a 100644 --- a/front/src/Connexion/LocalUserStore.ts +++ b/front/src/Connexion/LocalUserStore.ts @@ -9,7 +9,8 @@ const gameQualityKey = 'gameQuality'; const videoQualityKey = 'videoQuality'; const audioPlayerVolumeKey = 'audioVolume'; const audioPlayerMuteKey = 'audioMute'; -const helpCameraSettingsShown = 'helpCameraSettingsShown'; +const helpCameraSettingsShown = 'helpCameraSettingsShown'; +const joystickKey = 'showJoystick'; class LocalUserStore { saveUser(localUser: LocalUser) { @@ -100,6 +101,13 @@ class LocalUserStore { getHelpCameraSettingsShown(): boolean { return localStorage.getItem(helpCameraSettingsShown) === '1'; } + + setJoystick(value: boolean): void { + localStorage.setItem(joystickKey, value.toString()); + } + getJoystick(): boolean { + return localStorage.getItem(joystickKey) === 'true'; + } } export const localUserStore = new LocalUserStore(); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 24511582..5d7d173f 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -51,6 +51,10 @@ import {Room} from "../../Connexion/Room"; import {jitsiFactory} from "../../WebRtc/JitsiFactory"; import {urlManager} from "../../Url/UrlManager"; import {audioManager} from "../../WebRtc/AudioManager"; +import {IVirtualJoystick} from "../../types"; +const { + default: VirtualJoystick, +} = require("phaser3-rex-plugins/plugins/virtualjoystick.js"); import {PresentationModeIcon} from "../Components/PresentationModeIcon"; import {ChatModeIcon} from "../Components/ChatModeIcon"; import {OpenChatIcon, openChatIconName} from "../Components/OpenChatIcon"; @@ -164,6 +168,7 @@ export class GameScene extends ResizableScene implements CenterListener { private messageSubscription: Subscription|null = null; private popUpElements : Map = new Map(); private originalMapUrl: string|undefined; + public virtualJoystick!: IVirtualJoystick; constructor(private room: Room, MapUrlFile: string, customKey?: string|undefined) { super({ @@ -183,6 +188,16 @@ export class GameScene extends ResizableScene implements CenterListener { this.connectionAnswerPromise = new Promise((resolve, reject): void => { this.connectionAnswerPromiseResolve = resolve; }) + const joystickVisible = localUserStore.getJoystick(); + if (joystickVisible) { + const canvas = document.querySelector('canvas') + canvas?.addEventListener('click', () => { + const body = document.querySelector('body') + body?.requestFullscreen() + }, { + once: true + }) + } } //hook preload scene @@ -399,9 +414,19 @@ export class GameScene extends ResizableScene implements CenterListener { //initialise list of other player this.MapPlayers = this.physics.add.group({immovable: true}); + this.virtualJoystick = new VirtualJoystick(this, { + x: this.game.renderer.width / 2, + y: this.game.renderer.height / 2, + radius: 20, + base: this.add.circle(0, 0, 20, 0x888888), + thumb: this.add.circle(0, 0, 10, 0xcccccc), + enable: true, + dir: "8dir", + }); + this.virtualJoystick.visible = localUserStore.getJoystick() //create input to move - this.userInputManager = new UserInputManager(this); mediaManager.setUserInputManager(this.userInputManager); + this.userInputManager = new UserInputManager(this, this.virtualJoystick); //notify game manager can to create currentUser in map this.createCurrentPlayer(); diff --git a/front/src/Phaser/Menu/MenuScene.ts b/front/src/Phaser/Menu/MenuScene.ts index f29fd39d..cdbe13f5 100644 --- a/front/src/Phaser/Menu/MenuScene.ts +++ b/front/src/Phaser/Menu/MenuScene.ts @@ -291,6 +291,9 @@ export class MenuScene extends Phaser.Scene { case 'editGameSettingsButton': this.openGameSettingsMenu(); break; + case 'showJoystick': + this.showJoystick(); + break; case 'adminConsoleButton': gameManager.getCurrentGameScene(this).ConsoleGlobalMessageManager.activeMessageConsole(); break; @@ -328,4 +331,21 @@ export class MenuScene extends Phaser.Scene { this.closeGameShare(); this.gameReportElement.close(); } + + private showJoystick() { + const gameScene = gameManager.getCurrentGameScene(this) + if (gameScene?.virtualJoystick) { + const joystickVisible = !gameScene.virtualJoystick.visible + gameScene.virtualJoystick.visible = joystickVisible + localUserStore.setJoystick(joystickVisible) + } + const body = document.querySelector('body') + if (body) { + if (document.fullscreenElement ?? document.fullscreen) { + document.exitFullscreen() + } else { + body.requestFullscreen(); + } + } + } } diff --git a/front/src/Phaser/UserInput/UserInputManager.ts b/front/src/Phaser/UserInput/UserInputManager.ts index 2e686d8e..9f6c76f3 100644 --- a/front/src/Phaser/UserInput/UserInputManager.ts +++ b/front/src/Phaser/UserInput/UserInputManager.ts @@ -1,4 +1,8 @@ +import { Direction, IVirtualJoystick } from "../../types"; import {GameScene} from "../Game/GameScene"; +const { + default: VirtualJoystick, +} = require("phaser3-rex-plugins/plugins/virtualjoystick.js"); interface UserInputManagerDatum { keyInstance: Phaser.Input.Keyboard.Key; @@ -25,6 +29,9 @@ export class ActiveEventList { set(event: UserInputEvent, value: boolean): void { this.KeysCode.set(event, value); } + forEach(callback: (value: boolean, key: UserInputEvent) => void): void { + this.KeysCode.forEach(callback); + } } //this class is responsible for catching user inputs and listing all active user actions at every game tick events. @@ -32,10 +39,32 @@ export class UserInputManager { private KeysCode!: UserInputManagerDatum[]; private Scene: GameScene; private isInputDisabled : boolean; - constructor(Scene : GameScene) { + private joystickEvents = new ActiveEventList(); + + constructor(Scene: GameScene, virtualJoystick: IVirtualJoystick) { this.Scene = Scene; - this.initKeyBoardEvent(); this.isInputDisabled = false; + this.initKeyBoardEvent(); + virtualJoystick.on("update", () => { + const cursorKeys = virtualJoystick.createCursorKeys(); + for (const name in cursorKeys) { + const key = cursorKeys[name as Direction]; + switch (name) { + case "up": + this.joystickEvents.set(UserInputEvent.MoveUp, key.isDown); + break; + case "left": + this.joystickEvents.set(UserInputEvent.MoveLeft, key.isDown); + break; + case "down": + this.joystickEvents.set(UserInputEvent.MoveDown, key.isDown); + break; + case "right": + this.joystickEvents.set(UserInputEvent.MoveRight, key.isDown); + break; + } + } + }); } initKeyBoardEvent(){ @@ -78,11 +107,15 @@ export class UserInputManager { if (this.isInputDisabled) { return eventsMap; } + this.joystickEvents.forEach((value, key) => { + if (value) { + eventsMap.set(key, value); + } + }); this.KeysCode.forEach(d => { if (d. keyInstance.isDown) { eventsMap.set(d.event, true); } - }); return eventsMap; } diff --git a/front/src/types.ts b/front/src/types.ts new file mode 100644 index 00000000..d65b7f5a --- /dev/null +++ b/front/src/types.ts @@ -0,0 +1,22 @@ +import Phaser from "phaser"; + +export type CursorKey = { + isDown: boolean +} + +export type Direction = 'left' | 'right' | 'up' | 'down' + +export interface CursorKeys extends Record { + left: CursorKey; + right: CursorKey; + up: CursorKey; + down: CursorKey; +} + +export interface IVirtualJoystick extends Phaser.GameObjects.GameObject { + y: number; + x: number; + visible: boolean; + createCursorKeys: () => CursorKeys; +} + diff --git a/front/tsconfig.json b/front/tsconfig.json index b5c8c74d..fea8fd54 100644 --- a/front/tsconfig.json +++ b/front/tsconfig.json @@ -9,17 +9,18 @@ "downlevelIteration": true, "jsx": "react", "allowJs": true, + "esModuleInterop": true, - "strict": true, /* Enable all strict type-checking options. */ - "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ - "strictNullChecks": true, /* Enable strict null checks. */ - "strictFunctionTypes": true, /* Enable strict checking of function types. */ - "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ - "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ - "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ - "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + "strict": true, /* Enable all strict type-checking options. */ + "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */ + "strictNullChecks": true, /* Enable strict null checks. */ + "strictFunctionTypes": true, /* Enable strict checking of function types. */ + "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ - "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ - "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */ + "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + "noFallthroughCasesInSwitch": true /* Report errors for fallthrough cases in switch statement. */ } } diff --git a/front/yarn.lock b/front/yarn.lock index eac99e4e..26573239 100644 --- a/front/yarn.lock +++ b/front/yarn.lock @@ -1819,6 +1819,11 @@ eventemitter3@^2.0.3: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-2.0.3.tgz#b5e1079b59fb5e1ba2771c0a993be060a58c99ba" integrity sha1-teEHm1n7XhuidxwKmTvgYKWMmbo= +eventemitter3@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7" + integrity sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q== + eventemitter3@^4.0.0, eventemitter3@^4.0.4: version "4.0.7" resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" @@ -3064,6 +3069,11 @@ loglevel@^1.6.8: resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.0.tgz#728166855a740d59d38db01cf46f042caa041bb0" integrity sha512-i2sY04nal5jDcagM3FMfG++T69GEEM8CYuOfeOIvmXzOIcwE9a/CJPR0MFM97pYMj/u10lzz7/zd7+qwhrBTqQ== +lokijs@^1.5.11: + version "1.5.11" + resolved "https://registry.yarnpkg.com/lokijs/-/lokijs-1.5.11.tgz#2b2ea82ec66050e4b112c6cfc588dac22d362b13" + integrity sha512-YYyuBPxMn/oS0tFznQDbIX5XL1ltMcwFqCboDr8voYE4VCDzR5vAsrvQDhlnua4lBeqMqHmLvUXRTmRUzUKH1Q== + lower-case@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.1.tgz#39eeb36e396115cc05e29422eaea9e692c9408c7" @@ -3625,6 +3635,11 @@ pako@~1.0.5: resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== +papaparse@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/papaparse/-/papaparse-5.3.0.tgz#ab1702feb96e79ab4309652f36db9536563ad05a" + integrity sha512-Lb7jN/4bTpiuGPrYy4tkKoUS8sTki8zacB5ke1p5zolhcSE4TlWgrlsxjrDTbG/dFVh07ck7X36hUf/b5V68pg== + parallel-transform@^1.1.0: version "1.2.0" resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc" @@ -3752,6 +3767,16 @@ pbkdf2@^3.0.3: safe-buffer "^5.0.1" sha.js "^2.4.8" +phaser3-rex-plugins@^1.1.42: + version "1.1.44" + resolved "https://registry.yarnpkg.com/phaser3-rex-plugins/-/phaser3-rex-plugins-1.1.44.tgz#83588ab2801c5b3a80a18be4f0ae37f1f32096b0" + integrity sha512-JPr3+UQv4+uv4etZr80aFhYxw2dk4UYG9/gQ4uMSSXQuc8gXQkXCr3J3zm8LJqH+1FXeK8nncpC7TKa4uKLgQw== + dependencies: + eventemitter3 "^3.1.2" + lokijs "^1.5.11" + papaparse "^5.3.0" + webfontloader "^1.6.28" + phaser@3.24.1: version "3.24.1" resolved "https://registry.yarnpkg.com/phaser/-/phaser-3.24.1.tgz#376e0c965d2a35af37c06ee78627dafbde5be017" @@ -5144,6 +5169,11 @@ wbuf@^1.1.0, wbuf@^1.7.3: dependencies: minimalistic-assert "^1.0.0" +webfontloader@^1.6.28: + version "1.6.28" + resolved "https://registry.yarnpkg.com/webfontloader/-/webfontloader-1.6.28.tgz#db786129253cb6e8eae54c2fb05f870af6675bae" + integrity sha1-23hhKSU8tujq5UwvsF+HCvZnW64= + webpack-cli@^3.3.11: version "3.3.12" resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-3.3.12.tgz#94e9ada081453cd0aa609c99e500012fd3ad2d4a" From e7131204342dde9421edfd5db46072a894d0a46b Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Fri, 22 Jan 2021 23:25:39 +0100 Subject: [PATCH 31/66] Reposition joystick (thx @TabascoEye) --- front/src/Phaser/Game/GameScene.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 5d7d173f..22ce0f42 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -428,6 +428,12 @@ export class GameScene extends ResizableScene implements CenterListener { mediaManager.setUserInputManager(this.userInputManager); this.userInputManager = new UserInputManager(this, this.virtualJoystick); + // Listener event to reposition virtual joystick + // whatever place you click in game area + this.input.on('pointerdown', (pointer: { x: number; y: number; }) => { + this.virtualJoystick.x = pointer.x; + this.virtualJoystick.y = pointer.y; + }); //notify game manager can to create currentUser in map this.createCurrentPlayer(); this.removeAllRemotePlayers(); //cleanup the list of remote players in case the scene was rebooted From 9c9d262782dd8cce82c17aa1ad9120707d06b3aa Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Sat, 23 Jan 2021 02:21:16 +0100 Subject: [PATCH 32/66] Make movement speed depend on joystick force --- front/src/Phaser/Player/Player.ts | 16 ++++--- .../src/Phaser/UserInput/UserInputManager.ts | 48 +++++++++++++++---- front/src/types.ts | 2 + 3 files changed, 50 insertions(+), 16 deletions(-) diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index bb961115..438f1228 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -47,7 +47,7 @@ export class Player extends Character implements CurrentGamerInterface { let x = 0; let y = 0; if (activeEvents.get(UserInputEvent.MoveUp)) { - y = - moveAmount; + y = -moveAmount; direction = PlayerAnimationDirections.Up; moving = true; } else if (activeEvents.get(UserInputEvent.MoveDown)) { @@ -64,16 +64,18 @@ export class Player extends Character implements CurrentGamerInterface { direction = PlayerAnimationDirections.Right; moving = true; } + moving = moving || activeEvents.get(UserInputEvent.JoystickMove); if (x !== 0 || y !== 0) { this.move(x, y); this.emit(hasMovedEventName, {moving, direction, x: this.x, y: this.y}); - } else { - if (this.wasMoving) { - //direction = PlayerAnimationNames.None; - this.stop(); - this.emit(hasMovedEventName, {moving, direction: this.previousDirection, x: this.x, y: this.y}); - } + } else if (this.wasMoving && moving) { + // slow joystick movement + this.move(0, 0); + this.emit(hasMovedEventName, {moving, direction: this.previousDirection, x: this.x, y: this.y}); + } else if (this.wasMoving && !moving) { + this.stop(); + this.emit(hasMovedEventName, {moving, direction: this.previousDirection, x: this.x, y: this.y}); } if (direction !== null) { diff --git a/front/src/Phaser/UserInput/UserInputManager.ts b/front/src/Phaser/UserInput/UserInputManager.ts index 9f6c76f3..c3f2c0bb 100644 --- a/front/src/Phaser/UserInput/UserInputManager.ts +++ b/front/src/Phaser/UserInput/UserInputManager.ts @@ -17,20 +17,24 @@ export enum UserInputEvent { SpeedUp, Interact, Shout, + JoystickMove, } -//we cannot the map structure so we have to create a replacment +//we cannot use a map structure so we have to create a replacment export class ActiveEventList { - private KeysCode : Map = new Map(); + private eventMap : Map = new Map(); get(event: UserInputEvent): boolean { - return this.KeysCode.get(event) || false; + return this.eventMap.get(event) || false; } set(event: UserInputEvent, value: boolean): void { - this.KeysCode.set(event, value); + this.eventMap.set(event, value); } forEach(callback: (value: boolean, key: UserInputEvent) => void): void { - this.KeysCode.forEach(callback); + this.eventMap.forEach(callback); + } + any(): boolean { + return Array.from(this.eventMap.values()).reduce((accu, curr) => accu || curr, false); } } @@ -39,14 +43,22 @@ export class UserInputManager { private KeysCode!: UserInputManagerDatum[]; private Scene: GameScene; private isInputDisabled : boolean; + + private joystick : IVirtualJoystick; private joystickEvents = new ActiveEventList(); + private joystickForceThreshold = 60; + private joystickForceAccuX = 0; + private joystickForceAccuY = 0; constructor(Scene: GameScene, virtualJoystick: IVirtualJoystick) { this.Scene = Scene; this.isInputDisabled = false; this.initKeyBoardEvent(); - virtualJoystick.on("update", () => { - const cursorKeys = virtualJoystick.createCursorKeys(); + this.joystick = virtualJoystick; + this.joystick.on("update", () => { + this.joystickForceAccuX = this.joystick.forceX ? this.joystickForceAccuX : 0; + this.joystickForceAccuY = this.joystick.forceY ? this.joystickForceAccuY : 0; + const cursorKeys = this.joystick.createCursorKeys(); for (const name in cursorKeys) { const key = cursorKeys[name as Direction]; switch (name) { @@ -109,11 +121,29 @@ export class UserInputManager { } this.joystickEvents.forEach((value, key) => { if (value) { - eventsMap.set(key, value); + switch (key) { + case UserInputEvent.MoveUp: + case UserInputEvent.MoveDown: + this.joystickForceAccuY += this.joystick.forceY; + if (Math.abs(this.joystickForceAccuY) > this.joystickForceThreshold) { + eventsMap.set(key, value); + this.joystickForceAccuY = 0; + } + break; + case UserInputEvent.MoveLeft: + case UserInputEvent.MoveRight: + this.joystickForceAccuX += this.joystick.forceX; + if (Math.abs(this.joystickForceAccuX) > this.joystickForceThreshold) { + eventsMap.set(key, value); + this.joystickForceAccuX = 0; + } + break; + } } }); + eventsMap.set(UserInputEvent.JoystickMove, this.joystickEvents.any()); this.KeysCode.forEach(d => { - if (d. keyInstance.isDown) { + if (d.keyInstance.isDown) { eventsMap.set(d.event, true); } }); diff --git a/front/src/types.ts b/front/src/types.ts index d65b7f5a..6b99434d 100644 --- a/front/src/types.ts +++ b/front/src/types.ts @@ -16,6 +16,8 @@ export interface CursorKeys extends Record { export interface IVirtualJoystick extends Phaser.GameObjects.GameObject { y: number; x: number; + forceX: number; + forceY: number; visible: boolean; createCursorKeys: () => CursorKeys; } From 340caaff322430b281362932ad4bd7f2b460e089 Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Wed, 7 Apr 2021 13:30:51 +0200 Subject: [PATCH 33/66] Make virtual joystick transparent --- front/src/Phaser/Game/GameScene.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 22ce0f42..428d9c0d 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -418,8 +418,8 @@ export class GameScene extends ResizableScene implements CenterListener { x: this.game.renderer.width / 2, y: this.game.renderer.height / 2, radius: 20, - base: this.add.circle(0, 0, 20, 0x888888), - thumb: this.add.circle(0, 0, 10, 0xcccccc), + base: this.add.circle(0, 0, 20), + thumb: this.add.circle(0, 0, 10), enable: true, dir: "8dir", }); From 633bdfcaffb6a6dc746a3105cc35c61015a8ac8c Mon Sep 17 00:00:00 2001 From: PizZaKatZe Date: Wed, 7 Apr 2021 12:42:56 +0200 Subject: [PATCH 34/66] Always enable virtual joystick Keeps 'toggle fullscreen' as a separate game setting. --- front/dist/resources/html/gameMenu.html | 2 +- front/src/Connexion/LocalUserStore.ts | 10 +++++----- front/src/Phaser/Game/GameScene.ts | 17 ++++++----------- front/src/Phaser/Menu/MenuScene.ts | 12 +++--------- 4 files changed, 15 insertions(+), 26 deletions(-) diff --git a/front/dist/resources/html/gameMenu.html b/front/dist/resources/html/gameMenu.html index fa46090a..ce740ec5 100644 --- a/front/dist/resources/html/gameMenu.html +++ b/front/dist/resources/html/gameMenu.html @@ -37,7 +37,7 @@
- +
diff --git a/front/src/Connexion/LocalUserStore.ts b/front/src/Connexion/LocalUserStore.ts index 11977f7a..ace7b17e 100644 --- a/front/src/Connexion/LocalUserStore.ts +++ b/front/src/Connexion/LocalUserStore.ts @@ -10,7 +10,7 @@ const videoQualityKey = 'videoQuality'; const audioPlayerVolumeKey = 'audioVolume'; const audioPlayerMuteKey = 'audioMute'; const helpCameraSettingsShown = 'helpCameraSettingsShown'; -const joystickKey = 'showJoystick'; +const fullscreenKey = 'fullscreen'; class LocalUserStore { saveUser(localUser: LocalUser) { @@ -102,11 +102,11 @@ class LocalUserStore { return localStorage.getItem(helpCameraSettingsShown) === '1'; } - setJoystick(value: boolean): void { - localStorage.setItem(joystickKey, value.toString()); + setFullscreen(value: boolean): void { + localStorage.setItem(fullscreenKey, value.toString()); } - getJoystick(): boolean { - return localStorage.getItem(joystickKey) === 'true'; + getFullscreen(): boolean { + return localStorage.getItem(fullscreenKey) === 'true'; } } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 428d9c0d..294174a2 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -188,16 +188,6 @@ export class GameScene extends ResizableScene implements CenterListener { this.connectionAnswerPromise = new Promise((resolve, reject): void => { this.connectionAnswerPromiseResolve = resolve; }) - const joystickVisible = localUserStore.getJoystick(); - if (joystickVisible) { - const canvas = document.querySelector('canvas') - canvas?.addEventListener('click', () => { - const body = document.querySelector('body') - body?.requestFullscreen() - }, { - once: true - }) - } } //hook preload scene @@ -423,7 +413,7 @@ export class GameScene extends ResizableScene implements CenterListener { enable: true, dir: "8dir", }); - this.virtualJoystick.visible = localUserStore.getJoystick() + this.virtualJoystick.visible = true; //create input to move mediaManager.setUserInputManager(this.userInputManager); this.userInputManager = new UserInputManager(this, this.virtualJoystick); @@ -434,6 +424,11 @@ export class GameScene extends ResizableScene implements CenterListener { this.virtualJoystick.x = pointer.x; this.virtualJoystick.y = pointer.y; }); + + if (localUserStore.getFullscreen()) { + document.querySelector('body')?.requestFullscreen(); + } + //notify game manager can to create currentUser in map this.createCurrentPlayer(); this.removeAllRemotePlayers(); //cleanup the list of remote players in case the scene was rebooted diff --git a/front/src/Phaser/Menu/MenuScene.ts b/front/src/Phaser/Menu/MenuScene.ts index cdbe13f5..b42b575f 100644 --- a/front/src/Phaser/Menu/MenuScene.ts +++ b/front/src/Phaser/Menu/MenuScene.ts @@ -291,8 +291,8 @@ export class MenuScene extends Phaser.Scene { case 'editGameSettingsButton': this.openGameSettingsMenu(); break; - case 'showJoystick': - this.showJoystick(); + case 'toggleFullscreen': + this.toggleFullscreen(); break; case 'adminConsoleButton': gameManager.getCurrentGameScene(this).ConsoleGlobalMessageManager.activeMessageConsole(); @@ -332,13 +332,7 @@ export class MenuScene extends Phaser.Scene { this.gameReportElement.close(); } - private showJoystick() { - const gameScene = gameManager.getCurrentGameScene(this) - if (gameScene?.virtualJoystick) { - const joystickVisible = !gameScene.virtualJoystick.visible - gameScene.virtualJoystick.visible = joystickVisible - localUserStore.setJoystick(joystickVisible) - } + private toggleFullscreen() { const body = document.querySelector('body') if (body) { if (document.fullscreenElement ?? document.fullscreen) { From f82551016a30e37402285b23087f20997454fda6 Mon Sep 17 00:00:00 2001 From: kharhamel Date: Tue, 13 Apr 2021 11:24:57 +0200 Subject: [PATCH 35/66] FIX: in vertical mode, the coWebSite now open from the top --- front/dist/resources/style/cowebsite.scss | 13 +++++++++---- front/src/WebRtc/CoWebsiteManager.ts | 4 ++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/front/dist/resources/style/cowebsite.scss b/front/dist/resources/style/cowebsite.scss index 33a6b6bf..b752728d 100644 --- a/front/dist/resources/style/cowebsite.scss +++ b/front/dist/resources/style/cowebsite.scss @@ -88,21 +88,26 @@ } } @media (max-aspect-ratio: 1/1) { + + #main-container { + display: flex; + flex-direction: column-reverse; + } #cowebsite { left: 0; - bottom: 0; + top: 0; width: 100%; height: 50%; display: flex; - flex-direction: column; + flex-direction: column-reverse; &.loading { - transform: translateY(90%); + transform: translateY(-90%); } &.hidden { - transform: translateY(100%); + transform: translateY(-100%); } main { diff --git a/front/src/WebRtc/CoWebsiteManager.ts b/front/src/WebRtc/CoWebsiteManager.ts index e352d70c..95885a53 100644 --- a/front/src/WebRtc/CoWebsiteManager.ts +++ b/front/src/WebRtc/CoWebsiteManager.ts @@ -32,7 +32,7 @@ class CoWebsiteManager { private resizing: boolean = false; private cowebsiteMainDom: HTMLDivElement; private cowebsiteAsideDom: HTMLDivElement; - + get width(): number { return this.cowebsiteDiv.clientWidth; } @@ -74,7 +74,7 @@ class CoWebsiteManager { private initResizeListeners() { const movecallback = (event:MouseEvent) => { - this.verticalMode ? this.height -= event.movementY / this.getDevicePixelRatio() : this.width -= event.movementX / this.getDevicePixelRatio(); + this.verticalMode ? this.height += event.movementY / this.getDevicePixelRatio() : this.width -= event.movementX / this.getDevicePixelRatio(); this.fire(); } From 5ebb0c94e6737e1b83a9b4a2f0e024b1620bb3ab Mon Sep 17 00:00:00 2001 From: kharhamel Date: Wed, 14 Apr 2021 11:56:54 +0200 Subject: [PATCH 36/66] FIX: calling emitPlayGlobalMessage in pusher without the admin tag will throw an error --- back/src/Services/SocketManager.ts | 13 ------------- pusher/src/Services/SocketManager.ts | 4 ++++ 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/back/src/Services/SocketManager.ts b/back/src/Services/SocketManager.ts index 4a76f131..647afc95 100644 --- a/back/src/Services/SocketManager.ts +++ b/back/src/Services/SocketManager.ts @@ -510,19 +510,6 @@ export class SocketManager { return this.rooms; } - /** - * - * @param token - */ - /*searchClientByUuid(uuid: string): ExSocketInterface | null { - for(const socket of this.sockets.values()){ - if(socket.userUuid === uuid){ - return socket; - } - } - return null; - }*/ - public handleQueryJitsiJwtMessage(user: User, queryJitsiJwtMessage: QueryJitsiJwtMessage) { const room = queryJitsiJwtMessage.getJitsiroom(); diff --git a/pusher/src/Services/SocketManager.ts b/pusher/src/Services/SocketManager.ts index 6efd6f8d..726e11a1 100644 --- a/pusher/src/Services/SocketManager.ts +++ b/pusher/src/Services/SocketManager.ts @@ -364,6 +364,10 @@ export class SocketManager implements ZoneEventListener { } emitPlayGlobalMessage(client: ExSocketInterface, playglobalmessage: PlayGlobalMessage) { + if (!client.tags.includes('admin')) { + //In case of xss injection, we just kill the connection. + throw 'Client is not an admin!'; + } const pusherToBackMessage = new PusherToBackMessage(); pusherToBackMessage.setPlayglobalmessage(playglobalmessage); From ce2c5e0cb55af3d452b30b92120b84c37b70b3bf Mon Sep 17 00:00:00 2001 From: kharhamel Date: Wed, 14 Apr 2021 15:36:25 +0200 Subject: [PATCH 37/66] FEATURE: added the env variable MAX_PER_GROUP --- .env.template | 4 ++++ back/src/Enum/EnvironmentVariable.ts | 1 + back/src/Model/Group.ts | 4 ++-- docker-compose.single-domain.yaml | 1 + docker-compose.yaml | 3 +++ front/src/Enum/EnvironmentVariable.ts | 1 + front/src/Phaser/Game/GameScene.ts | 11 +++++++++-- 7 files changed, 21 insertions(+), 4 deletions(-) diff --git a/.env.template b/.env.template index a54df82e..a9ae63d7 100644 --- a/.env.template +++ b/.env.template @@ -13,3 +13,7 @@ TURN_STATIC_AUTH_SECRET= # The email address used by Let's encrypt to send renewal warnings (compulsory) ACME_EMAIL= + +MAX_PER_GROUP=4 +MAX_USERNAME_LENGTH=8 + diff --git a/back/src/Enum/EnvironmentVariable.ts b/back/src/Enum/EnvironmentVariable.ts index 8736a856..81693a98 100644 --- a/back/src/Enum/EnvironmentVariable.ts +++ b/back/src/Enum/EnvironmentVariable.ts @@ -11,6 +11,7 @@ const HTTP_PORT = parseInt(process.env.HTTP_PORT || '8080') || 8080; const GRPC_PORT = parseInt(process.env.GRPC_PORT || '50051') || 50051; export const SOCKET_IDLE_TIMER = parseInt(process.env.SOCKET_IDLE_TIMER as string) || 30; // maximum time (in second) without activity before a socket is closed export const TURN_STATIC_AUTH_SECRET = process.env.TURN_STATIC_AUTH_SECRET || ''; +export const MAX_PER_GROUP = parseInt(process.env.MAX_PER_GROUP || '4'); export { MINIMUM_DISTANCE, diff --git a/back/src/Model/Group.ts b/back/src/Model/Group.ts index f26a0e0d..ffe7a78a 100644 --- a/back/src/Model/Group.ts +++ b/back/src/Model/Group.ts @@ -4,9 +4,9 @@ import {PositionInterface} from "_Model/PositionInterface"; import {Movable} from "_Model/Movable"; import {PositionNotifier} from "_Model/PositionNotifier"; import {gaugeManager} from "../Services/GaugeManager"; +import {MAX_PER_GROUP} from "../Enum/EnvironmentVariable"; export class Group implements Movable { - static readonly MAX_PER_GROUP = 4; private static nextId: number = 1; @@ -88,7 +88,7 @@ export class Group implements Movable { } isFull(): boolean { - return this.users.size >= Group.MAX_PER_GROUP; + return this.users.size >= MAX_PER_GROUP; } isEmpty(): boolean { diff --git a/docker-compose.single-domain.yaml b/docker-compose.single-domain.yaml index 0bd1dcb6..1a390f4c 100644 --- a/docker-compose.single-domain.yaml +++ b/docker-compose.single-domain.yaml @@ -116,6 +116,7 @@ services: ADMIN_API_TOKEN: "$ADMIN_API_TOKEN" JITSI_URL: $JITSI_URL JITSI_ISS: $JITSI_ISS + MAX_PER_GROUP: "$MAX_PER_GROUP" volumes: - ./back:/usr/src/app labels: diff --git a/docker-compose.yaml b/docker-compose.yaml index 504c5b23..cea1bc03 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -38,6 +38,8 @@ services: TURN_USER: "" TURN_PASSWORD: "" START_ROOM_URL: "$START_ROOM_URL" + MAX_PER_GROUP: "$MAX_PER_GROUP" + MAX_USERNAME_LENGTH: "$MAX_USERNAME_LENGTH" command: yarn run start volumes: - ./front:/usr/src/app @@ -110,6 +112,7 @@ services: JITSI_URL: $JITSI_URL JITSI_ISS: $JITSI_ISS TURN_STATIC_AUTH_SECRET: SomeStaticAuthSecret + MAX_PER_GROUP: "MAX_PER_GROUP" volumes: - ./back:/usr/src/app labels: diff --git a/front/src/Enum/EnvironmentVariable.ts b/front/src/Enum/EnvironmentVariable.ts index 5040e59f..11479b0f 100644 --- a/front/src/Enum/EnvironmentVariable.ts +++ b/front/src/Enum/EnvironmentVariable.ts @@ -15,6 +15,7 @@ const ZOOM_LEVEL = 1/*3/4*/; const POSITION_DELAY = 200; // Wait 200ms between sending position events const MAX_EXTRAPOLATION_TIME = 100; // Extrapolate a maximum of 250ms if no new movement is sent by the player export const MAX_USERNAME_LENGTH = parseInt(process.env.MAX_USERNAME_LENGTH || '') || 8; +export const MAX_PER_GROUP = parseInt(process.env.MAX_PER_GROUP || '4'); export { DEBUG_MODE, diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 294174a2..e9028a00 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -10,7 +10,14 @@ import { RoomJoinedMessageInterface } from "../../Connexion/ConnexionModels"; import {CurrentGamerInterface, hasMovedEventName, Player} from "../Player/Player"; -import {DEBUG_MODE, JITSI_PRIVATE_MODE, POSITION_DELAY, RESOLUTION, ZOOM_LEVEL} from "../../Enum/EnvironmentVariable"; +import { + DEBUG_MODE, + JITSI_PRIVATE_MODE, + MAX_PER_GROUP, + POSITION_DELAY, + RESOLUTION, + ZOOM_LEVEL +} from "../../Enum/EnvironmentVariable"; import {ITiledMap, ITiledMapLayer, ITiledMapLayerProperty, ITiledMapObject, ITiledTileSet} from "../Map/ITiledMap"; import {AddPlayerInterface} from "./AddPlayerInterface"; import {PlayerAnimationDirections} from "../Player/Animation"; @@ -1344,7 +1351,7 @@ ${escapedMessage} this, Math.round(groupPositionMessage.position.x), Math.round(groupPositionMessage.position.y), - groupPositionMessage.groupSize === 4 ? 'circleSprite-red' : 'circleSprite-white' + groupPositionMessage.groupSize === MAX_PER_GROUP ? 'circleSprite-red' : 'circleSprite-white' ); sprite.setDisplayOrigin(48, 48); this.add.existing(sprite); From d7a74baa9d6344ce9e0a04df85f778f3196eef6f Mon Sep 17 00:00:00 2001 From: kharhamel Date: Wed, 14 Apr 2021 17:47:26 +0200 Subject: [PATCH 38/66] FEATURE: add the ability to zoom in and out using touch screen --- front/.eslintrc.json | 3 +-- front/src/Phaser/Game/GameManager.ts | 2 +- front/src/Phaser/Game/GameScene.ts | 10 +++++++--- front/src/Phaser/Login/EnableCameraScene.ts | 6 ++++++ front/src/Phaser/Login/LoginScene.ts | 5 +++++ front/src/Phaser/Login/SelectCharacterScene.ts | 8 ++++++-- front/src/Phaser/Login/SelectCompanionScene.ts | 6 ++++++ front/src/Phaser/UserInput/PinchManager.ts | 16 ++++++++++++++++ front/src/Touch/TouchScreenManager.ts | 16 ++++++++++++++++ front/src/index.ts | 3 +-- front/src/rex-plugins.d.ts | 12 ++++++++++++ 11 files changed, 77 insertions(+), 10 deletions(-) create mode 100644 front/src/Phaser/UserInput/PinchManager.ts create mode 100644 front/src/Touch/TouchScreenManager.ts create mode 100644 front/src/rex-plugins.d.ts diff --git a/front/.eslintrc.json b/front/.eslintrc.json index 3aab37d9..3ba19cb3 100644 --- a/front/.eslintrc.json +++ b/front/.eslintrc.json @@ -24,7 +24,6 @@ "@typescript-eslint" ], "rules": { - "no-unused-vars": "off", - "@typescript-eslint/no-explicit-any": "error" + "no-unused-vars": "off" } } diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts index c146c06d..da10a8ca 100644 --- a/front/src/Phaser/Game/GameManager.ts +++ b/front/src/Phaser/Game/GameManager.ts @@ -34,7 +34,7 @@ export class GameManager { public async init(scenePlugin: Phaser.Scenes.ScenePlugin): Promise { this.startRoom = await connectionManager.initGameConnexion(); await this.loadMap(this.startRoom, scenePlugin); - + if (!this.playerName) { return LoginSceneName; } else if (!this.characterLayers || !this.characterLayers.length) { diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 294174a2..f18a11d1 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -52,9 +52,7 @@ import {jitsiFactory} from "../../WebRtc/JitsiFactory"; import {urlManager} from "../../Url/UrlManager"; import {audioManager} from "../../WebRtc/AudioManager"; import {IVirtualJoystick} from "../../types"; -const { - default: VirtualJoystick, -} = require("phaser3-rex-plugins/plugins/virtualjoystick.js"); +import VirtualJoystick from 'phaser3-rex-plugins/plugins/virtualjoystick.js'; import {PresentationModeIcon} from "../Components/PresentationModeIcon"; import {ChatModeIcon} from "../Components/ChatModeIcon"; import {OpenChatIcon, openChatIconName} from "../Components/OpenChatIcon"; @@ -74,6 +72,8 @@ import DOMElement = Phaser.GameObjects.DOMElement; import {Subscription} from "rxjs"; import {worldFullMessageStream} from "../../Connexion/WorldFullMessageStream"; import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager"; +import {touchScreenManager} from "../../Touch/TouchScreenManager"; +import {PinchManager} from "../UserInput/PinchManager"; export interface GameSceneInitInterface { initPosition: PointInterface|null, @@ -351,6 +351,10 @@ export class GameScene extends ResizableScene implements CenterListener { urlManager.pushRoomIdToUrl(this.room); this.startLayerName = urlManager.getStartLayerNameFromUrl(); + if (touchScreenManager.supportTouchScreen) { + new PinchManager(this); + } + this.messageSubscription = worldFullMessageStream.stream.subscribe((message) => this.showWorldFullError()) const playerName = gameManager.getPlayerName(); diff --git a/front/src/Phaser/Login/EnableCameraScene.ts b/front/src/Phaser/Login/EnableCameraScene.ts index c3aa5077..6a91fc34 100644 --- a/front/src/Phaser/Login/EnableCameraScene.ts +++ b/front/src/Phaser/Login/EnableCameraScene.ts @@ -7,6 +7,8 @@ import {RESOLUTION} from "../../Enum/EnvironmentVariable"; import {SoundMeter} from "../Components/SoundMeter"; import {SoundMeterSprite} from "../Components/SoundMeterSprite"; import {HtmlUtils} from "../../WebRtc/HtmlUtils"; +import {touchScreenManager} from "../../Touch/TouchScreenManager"; +import {PinchManager} from "../UserInput/PinchManager"; export const EnableCameraSceneName = "EnableCameraScene"; enum LoginTextures { @@ -54,6 +56,10 @@ export class EnableCameraScene extends Phaser.Scene { } create() { + if (touchScreenManager.supportTouchScreen) { + new PinchManager(this); + } + this.textField = new TextField(this, this.game.renderer.width / 2, 20, 'Turn on your camera and microphone'); this.pressReturnField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height - 30, 'Touch here\n\n or \n\nPress enter to start'); diff --git a/front/src/Phaser/Login/LoginScene.ts b/front/src/Phaser/Login/LoginScene.ts index 4ff582b4..057cb6ae 100644 --- a/front/src/Phaser/Login/LoginScene.ts +++ b/front/src/Phaser/Login/LoginScene.ts @@ -7,6 +7,8 @@ import {ResizableScene} from "./ResizableScene"; import {isUserNameValid, maxUserNameLength} from "../../Connexion/LocalUser"; import { localUserStore } from "../../Connexion/LocalUserStore"; import Rectangle = Phaser.GameObjects.Rectangle; +import {touchScreenManager} from "../../Touch/TouchScreenManager"; +import {PinchManager} from "../UserInput/PinchManager"; //todo: put this constants in a dedicated file export const LoginSceneName = "LoginScene"; @@ -39,6 +41,9 @@ export class LoginScene extends ResizableScene { } create() { + if (touchScreenManager.supportTouchScreen) { + new PinchManager(this); + } this.nameInput = new TextInput(this, this.game.renderer.width / 2, 70, maxUserNameLength, this.name,(text: string) => { this.name = text; diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts index edd09a91..3c8d0281 100644 --- a/front/src/Phaser/Login/SelectCharacterScene.ts +++ b/front/src/Phaser/Login/SelectCharacterScene.ts @@ -4,13 +4,14 @@ import Image = Phaser.GameObjects.Image; import Rectangle = Phaser.GameObjects.Rectangle; import {EnableCameraSceneName} from "./EnableCameraScene"; import {CustomizeSceneName} from "./CustomizeScene"; -import {ResizableScene} from "./ResizableScene"; import {localUserStore} from "../../Connexion/LocalUserStore"; -import {loadAllDefaultModels, loadCustomTexture} from "../Entity/PlayerTexturesLoadingManager"; +import {loadAllDefaultModels} from "../Entity/PlayerTexturesLoadingManager"; import {addLoader} from "../Components/Loader"; import {BodyResourceDescriptionInterface} from "../Entity/PlayerTextures"; import {AbstractCharacterScene} from "./AbstractCharacterScene"; import {areCharacterLayersValid} from "../../Connexion/LocalUser"; +import {touchScreenManager} from "../../Touch/TouchScreenManager"; +import {PinchManager} from "../UserInput/PinchManager"; //todo: put this constants in a dedicated file @@ -66,6 +67,9 @@ export class SelectCharacterScene extends AbstractCharacterScene { } create() { + if (touchScreenManager.supportTouchScreen) { + new PinchManager(this); + } this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Select your character'); this.pressReturnField = new TextField( this, diff --git a/front/src/Phaser/Login/SelectCompanionScene.ts b/front/src/Phaser/Login/SelectCompanionScene.ts index c7ee56f1..aeacd0c2 100644 --- a/front/src/Phaser/Login/SelectCompanionScene.ts +++ b/front/src/Phaser/Login/SelectCompanionScene.ts @@ -8,6 +8,8 @@ import { EnableCameraSceneName } from "./EnableCameraScene"; import { localUserStore } from "../../Connexion/LocalUserStore"; import { CompanionResourceDescriptionInterface } from "../Companion/CompanionTextures"; import { getAllCompanionResources } from "../Companion/CompanionTexturesLoadingManager"; +import {touchScreenManager} from "../../Touch/TouchScreenManager"; +import {PinchManager} from "../UserInput/PinchManager"; export const SelectCompanionSceneName = "SelectCompanionScene"; @@ -54,6 +56,10 @@ export class SelectCompanionScene extends ResizableScene { } create() { + if (touchScreenManager.supportTouchScreen) { + new PinchManager(this); + } + this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Select your companion'); const confirmTouchAreaY = 115 + 32 * Math.ceil(this.companionModels.length / this.nbCharactersPerRow); diff --git a/front/src/Phaser/UserInput/PinchManager.ts b/front/src/Phaser/UserInput/PinchManager.ts new file mode 100644 index 00000000..f412b787 --- /dev/null +++ b/front/src/Phaser/UserInput/PinchManager.ts @@ -0,0 +1,16 @@ +import {Pinch} from "phaser3-rex-plugins/plugins/gestures.js"; + +export class PinchManager { + private scene: Phaser.Scene; + private pinch!: any; + + constructor(scene: Phaser.Scene) { + this.scene = scene; + this.pinch = new Pinch(scene); + + this.pinch.on('pinch', (pinch:any) => { + this.scene.cameras.main.zoom *= pinch.scaleFactor; + }); + } + +} \ No newline at end of file diff --git a/front/src/Touch/TouchScreenManager.ts b/front/src/Touch/TouchScreenManager.ts new file mode 100644 index 00000000..dcb56ded --- /dev/null +++ b/front/src/Touch/TouchScreenManager.ts @@ -0,0 +1,16 @@ + +class TouchScreenManager { + + readonly supportTouchScreen:boolean; + + constructor() { + this.supportTouchScreen = this.detectTouchscreen(); + } + + //found here: https://stackoverflow.com/questions/4817029/whats-the-best-way-to-detect-a-touch-screen-device-using-javascript#4819886 + detectTouchscreen(): boolean { + return (('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)); + } +} + +export const touchScreenManager = new TouchScreenManager(); \ No newline at end of file diff --git a/front/src/index.ts b/front/src/index.ts index aab45a9b..5a18951b 100644 --- a/front/src/index.ts +++ b/front/src/index.ts @@ -2,7 +2,7 @@ import 'phaser'; import GameConfig = Phaser.Types.Core.GameConfig; import "../dist/resources/style/index.scss"; -import {DEBUG_MODE, JITSI_URL, RESOLUTION} from "./Enum/EnvironmentVariable"; +import {DEBUG_MODE, RESOLUTION} from "./Enum/EnvironmentVariable"; import {LoginScene} from "./Phaser/Login/LoginScene"; import {ReconnectingScene} from "./Phaser/Reconnecting/ReconnectingScene"; import {SelectCharacterScene} from "./Phaser/Login/SelectCharacterScene"; @@ -17,7 +17,6 @@ import {HelpCameraSettingsScene} from "./Phaser/Menu/HelpCameraSettingsScene"; import {localUserStore} from "./Connexion/LocalUserStore"; import {ErrorScene} from "./Phaser/Reconnecting/ErrorScene"; import {iframeListener} from "./Api/IframeListener"; -import {discussionManager} from "./WebRtc/DiscussionManager"; const {width, height} = coWebsiteManager.getGameSize(); diff --git a/front/src/rex-plugins.d.ts b/front/src/rex-plugins.d.ts new file mode 100644 index 00000000..bb0816b6 --- /dev/null +++ b/front/src/rex-plugins.d.ts @@ -0,0 +1,12 @@ + +declare module 'phaser3-rex-plugins/plugins/virtualjoystick.js' { + const content: any; + export default content; +} +declare module 'phaser3-rex-plugins/plugins/gestures-plugin.js' { + const content: any; + export default content; +} +declare module 'phaser3-rex-plugins/plugins/gestures.js' { + export const Pinch: any; +} \ No newline at end of file From 5028a5442268b458ef7c0d9f29d2303bec15aa5d Mon Sep 17 00:00:00 2001 From: "DESKTOP-FMM8UI0\\CLV" Date: Mon, 19 Apr 2021 10:19:30 +0200 Subject: [PATCH 39/66] Unsubscribe to iframeEvents in CleanUpClosingScene --- front/src/Phaser/Game/GameScene.ts | 34 ++++++++++++++++++------------ 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 9a165eb5..9ecf9d43 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -133,7 +133,7 @@ export class GameScene extends ResizableScene implements CenterListener { // A promise that will resolve when the "create" method is called (signaling loading is ended) private createPromise: Promise; private createPromiseResolve!: (value?: void | PromiseLike) => void; - + private iframeSubscriptionList! : Array; MapUrlFile: string; RoomId: string; instance: string; @@ -716,7 +716,8 @@ export class GameScene extends ResizableScene implements CenterListener { } private listenToIframeEvents(): void { - iframeListener.openPopupStream.subscribe((openPopupEvent) => { + this.iframeSubscriptionList = []; + this.iframeSubscriptionList.push(iframeListener.openPopupStream.subscribe((openPopupEvent) => { let objectLayerSquare : ITiledMapObject; const targetObjectData = this.getObjectLayerData(openPopupEvent.targetObject); @@ -760,7 +761,6 @@ ${escapedMessage} } id++; } - this.tweens.add({ targets : domElement , scale : 1, @@ -769,9 +769,9 @@ ${escapedMessage} }); this.popUpElements.set(openPopupEvent.popupId, domElement); - }); + })); - iframeListener.closePopupStream.subscribe((closePopupEvent) => { + this.iframeSubscriptionList.push(iframeListener.closePopupStream.subscribe((closePopupEvent) => { const popUpElement = this.popUpElements.get(closePopupEvent.popupId); if (popUpElement === undefined) { console.error('Could not close popup with ID ', closePopupEvent.popupId,'. Maybe it has already been closed?'); @@ -787,23 +787,25 @@ ${escapedMessage} this.popUpElements.delete(closePopupEvent.popupId); }, }); - }); + })); - iframeListener.disablePlayerControlStream.subscribe(()=>{ + this.iframeSubscriptionList.push(iframeListener.disablePlayerControlStream.subscribe(()=>{ this.userInputManager.disableControls(); - }) - iframeListener.enablePlayerControlStream.subscribe(()=>{ + })); + + this.iframeSubscriptionList.push(iframeListener.enablePlayerControlStream.subscribe(()=>{ this.userInputManager.restoreControls(); - }) + })); let scriptedBubbleSprite : Sprite; - iframeListener.displayBubbleStream.subscribe(()=>{ + this.iframeSubscriptionList.push(iframeListener.displayBubbleStream.subscribe(()=>{ scriptedBubbleSprite = new Sprite(this,this.CurrentPlayer.x + 25,this.CurrentPlayer.y,'circleSprite-white'); scriptedBubbleSprite.setDisplayOrigin(48, 48); this.add.existing(scriptedBubbleSprite); - }) - iframeListener.removeBubbleStream.subscribe(()=>{ + })); + + this.iframeSubscriptionList.push(iframeListener.removeBubbleStream.subscribe(()=>{ scriptedBubbleSprite.destroy(); - }) + })); } @@ -848,6 +850,10 @@ ${escapedMessage} this.simplePeer.closeAllConnections(); this.simplePeer?.unregister(); this.messageSubscription?.unsubscribe(); + + for(const iframeEvents of this.iframeSubscriptionList){ + iframeEvents.unsubscribe(); + } } private removeAllRemotePlayers(): void { From d31021c02d3a584a79c9701b0fa504deba29cd2d Mon Sep 17 00:00:00 2001 From: "DESKTOP-FMM8UI0\\CLV" Date: Mon, 19 Apr 2021 14:19:16 +0200 Subject: [PATCH 40/66] Replace share screen logo --- front/dist/resources/logos/monitor-close.svg | 45 +------------------- front/dist/resources/logos/monitor.svg | 29 ++++++++----- 2 files changed, 20 insertions(+), 54 deletions(-) diff --git a/front/dist/resources/logos/monitor-close.svg b/front/dist/resources/logos/monitor-close.svg index 80056e2d..52f446d1 100644 --- a/front/dist/resources/logos/monitor-close.svg +++ b/front/dist/resources/logos/monitor-close.svg @@ -1,44 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - + \ No newline at end of file diff --git a/front/dist/resources/logos/monitor.svg b/front/dist/resources/logos/monitor.svg index d4b586c6..be50195b 100644 --- a/front/dist/resources/logos/monitor.svg +++ b/front/dist/resources/logos/monitor.svg @@ -1,15 +1,24 @@ - - + + - + + + + + + + + + + + From b9d9a788684032dd171ba404ece76afaf617ddfa Mon Sep 17 00:00:00 2001 From: "DESKTOP-FMM8UI0\\CLV" Date: Mon, 19 Apr 2021 14:42:49 +0200 Subject: [PATCH 41/66] Link changed in Tutov3 Change position of cam and buttons share/cam/Mic --- front/dist/resources/style/style.css | 6 +++--- maps/Tuto/tutoV3.json | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index ba5b6c07..68a59f78 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -136,7 +136,7 @@ body .message-info.warning{ #div-myCamVideo { position: absolute; right: 15px; - bottom: 15px; + bottom: 30px; border-radius: 15px 15px 15px 15px; } @@ -167,7 +167,7 @@ video#myCamVideo{ background: #666; box-shadow: 2px 2px 24px #444; border-radius: 48px; - transform: translateY(40px); + transform: translateY(20px); transition-timing-function: ease-in-out; bottom: 20px; } @@ -183,7 +183,7 @@ video#myCamVideo{ .btn-cam-action div:hover{ background: #407cf7; box-shadow: 4px 4px 48px #666; - transition: 280ms; + transition: 120ms; } .btn-micro{ transition: all .3s; diff --git a/maps/Tuto/tutoV3.json b/maps/Tuto/tutoV3.json index 0362ae1c..2ae43690 100644 --- a/maps/Tuto/tutoV3.json +++ b/maps/Tuto/tutoV3.json @@ -24,7 +24,7 @@ { "name":"exitUrl", "type":"string", - "value":"@\/tcm\/workadventure\/floor0" + "value":"\/@\/tcm\/workadventure\/floor0" }], "type":"tilelayer", "visible":true, @@ -355,7 +355,7 @@ "value":"scriptTuto.js" }], "renderorder":"right-down", - "tiledversion":"2021.03.23", + "tiledversion":"1.5.0", "tileheight":32, "tilesets":[ { From 7e6a8b477d434d4cb271f49d32261366df3967c0 Mon Sep 17 00:00:00 2001 From: "DESKTOP-FMM8UI0\\CLV" Date: Mon, 19 Apr 2021 15:16:56 +0200 Subject: [PATCH 42/66] Debug state of share Screen button --- front/src/WebRtc/MediaManager.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts index 9da4aa59..0c414a3f 100644 --- a/front/src/WebRtc/MediaManager.ts +++ b/front/src/WebRtc/MediaManager.ts @@ -309,12 +309,17 @@ export class MediaManager { } private enableScreenSharing() { - this.monitorClose.style.display = "none"; - this.monitor.style.display = "block"; - this.monitorBtn.classList.add("enabled"); this.getScreenMedia().then((stream) => { this.triggerStartedScreenSharingCallbacks(stream); + this.monitorClose.style.display = "none"; + this.monitor.style.display = "block"; + this.monitorBtn.classList.add("enabled"); + }, () => { + this.monitorClose.style.display = "block"; + this.monitor.style.display = "none"; + this.monitorBtn.classList.remove("enabled"); }); + } private disableScreenSharing() { From 56287a29583e46572a8aabbda24b6dca2bc1f537 Mon Sep 17 00:00:00 2001 From: kharhamel Date: Fri, 16 Apr 2021 18:49:04 +0200 Subject: [PATCH 43/66] put the virtual joystick into the userInputManager and restricted it to touchscreens --- front/.eslintrc.json | 3 +- front/src/Phaser/Game/GameScene.ts | 20 +------ front/src/Phaser/UserInput/PinchManager.ts | 12 +++-- .../src/Phaser/UserInput/UserInputManager.ts | 53 +++++++++++++------ front/src/rex-plugins.d.ts | 6 +-- 5 files changed, 54 insertions(+), 40 deletions(-) diff --git a/front/.eslintrc.json b/front/.eslintrc.json index 3ba19cb3..3aab37d9 100644 --- a/front/.eslintrc.json +++ b/front/.eslintrc.json @@ -24,6 +24,7 @@ "@typescript-eslint" ], "rules": { - "no-unused-vars": "off" + "no-unused-vars": "off", + "@typescript-eslint/no-explicit-any": "error" } } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index f18a11d1..cfefd548 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -408,26 +408,10 @@ export class GameScene extends ResizableScene implements CenterListener { //initialise list of other player this.MapPlayers = this.physics.add.group({immovable: true}); - this.virtualJoystick = new VirtualJoystick(this, { - x: this.game.renderer.width / 2, - y: this.game.renderer.height / 2, - radius: 20, - base: this.add.circle(0, 0, 20), - thumb: this.add.circle(0, 0, 10), - enable: true, - dir: "8dir", - }); - this.virtualJoystick.visible = true; + //create input to move mediaManager.setUserInputManager(this.userInputManager); - this.userInputManager = new UserInputManager(this, this.virtualJoystick); - - // Listener event to reposition virtual joystick - // whatever place you click in game area - this.input.on('pointerdown', (pointer: { x: number; y: number; }) => { - this.virtualJoystick.x = pointer.x; - this.virtualJoystick.y = pointer.y; - }); + this.userInputManager = new UserInputManager(this); if (localUserStore.getFullscreen()) { document.querySelector('body')?.requestFullscreen(); diff --git a/front/src/Phaser/UserInput/PinchManager.ts b/front/src/Phaser/UserInput/PinchManager.ts index f412b787..f7c445fa 100644 --- a/front/src/Phaser/UserInput/PinchManager.ts +++ b/front/src/Phaser/UserInput/PinchManager.ts @@ -2,14 +2,20 @@ import {Pinch} from "phaser3-rex-plugins/plugins/gestures.js"; export class PinchManager { private scene: Phaser.Scene; - private pinch!: any; + private pinch!: any; // eslint-disable-line constructor(scene: Phaser.Scene) { this.scene = scene; this.pinch = new Pinch(scene); - this.pinch.on('pinch', (pinch:any) => { - this.scene.cameras.main.zoom *= pinch.scaleFactor; + this.pinch.on('pinch', (pinch:any) => { // eslint-disable-line + let newZoom = this.scene.cameras.main.zoom * pinch.scaleFactor; + if (newZoom < 0.25) { + newZoom = 0.25; + } else if(newZoom > 2) { + newZoom = 2; + } + this.scene.cameras.main.setZoom(newZoom); }); } diff --git a/front/src/Phaser/UserInput/UserInputManager.ts b/front/src/Phaser/UserInput/UserInputManager.ts index c3f2c0bb..ddc3ffa8 100644 --- a/front/src/Phaser/UserInput/UserInputManager.ts +++ b/front/src/Phaser/UserInput/UserInputManager.ts @@ -1,5 +1,6 @@ import { Direction, IVirtualJoystick } from "../../types"; import {GameScene} from "../Game/GameScene"; +import {touchScreenManager} from "../../Touch/TouchScreenManager"; const { default: VirtualJoystick, } = require("phaser3-rex-plugins/plugins/virtualjoystick.js"); @@ -44,17 +45,39 @@ export class UserInputManager { private Scene: GameScene; private isInputDisabled : boolean; - private joystick : IVirtualJoystick; + private joystick!: IVirtualJoystick; private joystickEvents = new ActiveEventList(); private joystickForceThreshold = 60; private joystickForceAccuX = 0; private joystickForceAccuY = 0; - constructor(Scene: GameScene, virtualJoystick: IVirtualJoystick) { + constructor(Scene: GameScene) { this.Scene = Scene; this.isInputDisabled = false; this.initKeyBoardEvent(); - this.joystick = virtualJoystick; + if (touchScreenManager.supportTouchScreen) { + this.initVirtualJoystick(); + } + } + + initVirtualJoystick() { + this.joystick = new VirtualJoystick(this, { + x: this.Scene.game.renderer.width / 2, + y: this.Scene.game.renderer.height / 2, + radius: 20, + base: this.Scene.add.circle(0, 0, 20), + thumb: this.Scene.add.circle(0, 0, 10), + enable: true, + dir: "8dir", + }); + this.joystick.visible = true; + + // Listener event to reposition virtual joystick + // whatever place you click in game area + this.Scene.input.on('pointerdown', (pointer: { x: number; y: number; }) => { + this.joystick.x = pointer.x; + this.joystick.y = pointer.y; + }); this.joystick.on("update", () => { this.joystickForceAccuX = this.joystick.forceX ? this.joystickForceAccuX : 0; this.joystickForceAccuY = this.joystick.forceY ? this.joystickForceAccuY : 0; @@ -62,18 +85,18 @@ export class UserInputManager { for (const name in cursorKeys) { const key = cursorKeys[name as Direction]; switch (name) { - case "up": - this.joystickEvents.set(UserInputEvent.MoveUp, key.isDown); - break; - case "left": - this.joystickEvents.set(UserInputEvent.MoveLeft, key.isDown); - break; - case "down": - this.joystickEvents.set(UserInputEvent.MoveDown, key.isDown); - break; - case "right": - this.joystickEvents.set(UserInputEvent.MoveRight, key.isDown); - break; + case "up": + this.joystickEvents.set(UserInputEvent.MoveUp, key.isDown); + break; + case "left": + this.joystickEvents.set(UserInputEvent.MoveLeft, key.isDown); + break; + case "down": + this.joystickEvents.set(UserInputEvent.MoveDown, key.isDown); + break; + case "right": + this.joystickEvents.set(UserInputEvent.MoveRight, key.isDown); + break; } } }); diff --git a/front/src/rex-plugins.d.ts b/front/src/rex-plugins.d.ts index bb0816b6..7ba8f65b 100644 --- a/front/src/rex-plugins.d.ts +++ b/front/src/rex-plugins.d.ts @@ -1,12 +1,12 @@ declare module 'phaser3-rex-plugins/plugins/virtualjoystick.js' { - const content: any; + const content: any; // eslint-disable-line export default content; } declare module 'phaser3-rex-plugins/plugins/gestures-plugin.js' { - const content: any; + const content: any; // eslint-disable-line export default content; } declare module 'phaser3-rex-plugins/plugins/gestures.js' { - export const Pinch: any; + export const Pinch: any; // eslint-disable-line } \ No newline at end of file From 415d8f9466699d15d3a9e9de8afaff1c91cdef3d Mon Sep 17 00:00:00 2001 From: kharhamel Date: Tue, 20 Apr 2021 10:52:06 +0200 Subject: [PATCH 44/66] the joystick is now visible only when pointer is down --- front/src/Phaser/Game/GameScene.ts | 3 --- .../src/Phaser/UserInput/UserInputManager.ts | 23 +++++++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index cfefd548..0f31a519 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -51,8 +51,6 @@ import {Room} from "../../Connexion/Room"; import {jitsiFactory} from "../../WebRtc/JitsiFactory"; import {urlManager} from "../../Url/UrlManager"; import {audioManager} from "../../WebRtc/AudioManager"; -import {IVirtualJoystick} from "../../types"; -import VirtualJoystick from 'phaser3-rex-plugins/plugins/virtualjoystick.js'; import {PresentationModeIcon} from "../Components/PresentationModeIcon"; import {ChatModeIcon} from "../Components/ChatModeIcon"; import {OpenChatIcon, openChatIconName} from "../Components/OpenChatIcon"; @@ -168,7 +166,6 @@ export class GameScene extends ResizableScene implements CenterListener { private messageSubscription: Subscription|null = null; private popUpElements : Map = new Map(); private originalMapUrl: string|undefined; - public virtualJoystick!: IVirtualJoystick; constructor(private room: Room, MapUrlFile: string, customKey?: string|undefined) { super({ diff --git a/front/src/Phaser/UserInput/UserInputManager.ts b/front/src/Phaser/UserInput/UserInputManager.ts index ddc3ffa8..602afee7 100644 --- a/front/src/Phaser/UserInput/UserInputManager.ts +++ b/front/src/Phaser/UserInput/UserInputManager.ts @@ -1,9 +1,7 @@ import { Direction, IVirtualJoystick } from "../../types"; import {GameScene} from "../Game/GameScene"; import {touchScreenManager} from "../../Touch/TouchScreenManager"; -const { - default: VirtualJoystick, -} = require("phaser3-rex-plugins/plugins/virtualjoystick.js"); +import VirtualJoystick from 'phaser3-rex-plugins/plugins/virtualjoystick.js'; interface UserInputManagerDatum { keyInstance: Phaser.Input.Keyboard.Key; @@ -21,6 +19,9 @@ export enum UserInputEvent { JoystickMove, } +const outOfScreenX = -1000; +const outOfScreenY = -1000; + //we cannot use a map structure so we have to create a replacment export class ActiveEventList { private eventMap : Map = new Map(); @@ -62,22 +63,23 @@ export class UserInputManager { initVirtualJoystick() { this.joystick = new VirtualJoystick(this, { - x: this.Scene.game.renderer.width / 2, - y: this.Scene.game.renderer.height / 2, + x: outOfScreenX, + y: outOfScreenY, radius: 20, - base: this.Scene.add.circle(0, 0, 20), - thumb: this.Scene.add.circle(0, 0, 10), + base: this.Scene.add.circle(0, 0, 20, 0xdddddd), + thumb: this.Scene.add.circle(0, 0, 10, 0x000000), enable: true, dir: "8dir", }); - this.joystick.visible = true; - // Listener event to reposition virtual joystick - // whatever place you click in game area this.Scene.input.on('pointerdown', (pointer: { x: number; y: number; }) => { this.joystick.x = pointer.x; this.joystick.y = pointer.y; }); + this.Scene.input.on('pointerup', (pointer: { x: number; y: number; }) => { + this.joystick.x = outOfScreenX; + this.joystick.y = outOfScreenY; + }); this.joystick.on("update", () => { this.joystickForceAccuX = this.joystick.forceX ? this.joystickForceAccuX : 0; this.joystickForceAccuY = this.joystick.forceY ? this.joystickForceAccuY : 0; @@ -128,6 +130,7 @@ export class UserInputManager { this.Scene.input.keyboard.removeAllListeners(); } + //todo: should we also disable the joystick? disableControls(){ this.Scene.input.keyboard.removeAllKeys(); this.isInputDisabled = true; From 341b048d6c85a83b0acb8ec6702009e51555b969 Mon Sep 17 00:00:00 2001 From: kharhamel Date: Tue, 20 Apr 2021 11:40:39 +0200 Subject: [PATCH 45/66] improved the visuals of the joystick --- .../resources/objects/joystickSplitted.png | Bin 0 -> 12833 bytes .../objects/smallHandleFilledGrey.png | Bin 0 -> 3554 bytes front/src/Phaser/Components/MobileJoystick.ts | 35 ++++++++++++++++++ front/src/Phaser/Game/GameScene.ts | 8 +++- .../src/Phaser/UserInput/UserInputManager.ts | 27 ++------------ 5 files changed, 45 insertions(+), 25 deletions(-) create mode 100644 front/dist/resources/objects/joystickSplitted.png create mode 100644 front/dist/resources/objects/smallHandleFilledGrey.png create mode 100644 front/src/Phaser/Components/MobileJoystick.ts diff --git a/front/dist/resources/objects/joystickSplitted.png b/front/dist/resources/objects/joystickSplitted.png new file mode 100644 index 0000000000000000000000000000000000000000..04ae67a3bef04ab710b4793e0f9990609d5a4ed4 GIT binary patch literal 12833 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�ub=(88;&BQWopi zwP(dGR0xllWKe3(1@>{fuu+ysiCJAQfq7?lf05p84VO9JGdy$`Rz1c@k#?@1a<$8V z$7l+3$2mD42i-|K=-W+p!{2L`YKkT)cjMMIpz4x-&LL249#xl>#k2h-*8{#?4!b~l zuri@xOlgTGw}2gVRe^dA7v>^7Vg3e1PVI5%MsL{_eF~=y?T6sT{xvu63Iu84J-mbH}`YDnl_d%d?1sf5( z1(7C~n>W9oY(CA&f*A?5kshjaXbV@s_ai-igbfEMn<4a{mW=hQyMRh_0kxrkMo9;@ zvoG-@AlkFV%_~79EAO)$bd}m*>w>KR`kTO9pOLvyy8QKpt@-ShiVSb#Krpw$*oBKe zHU0mfeY{TfKsjm7SU)%m$kC4kT5GOtjt>XrpnZ^%40BtvR*tMmPhFw=&41PyAmcU@Kwzfqx~Bv7li$>ht1 zNFRIRple^}?pLaVu(__v^V{b-4&m48_^xab(HF-Ky}dr|=5^(GY8Q!_nK_G9nEu)h zp8;>me#~?0$8{M;F?An-fz1wXju!WHXnY@tZYK*fJeYKUEWYm)#24I-U(2`Z{;>OX z^qTOHrc{lrqABGOvg-wu?;0>dUJ*;lu z<%euYLro4iyWWVPja#5bc;0k#vpzKKT{t_UK`N!V^XvBzMc{~oMU&D!V(M@T0UR@Ph17qDe_rgPmyrKhFC|MP}85U(L=IX5<|e%;E4+_ zp#7s1oPKxnB`O0cc>-xb;(#dlQoECL1xG$YXuh{*4XLyH%48N%iNtZ5X<((h3!&0yI>I3mBHa7Mc>-1G9Vvvf7JHmi$d<9b@BW2&5wRujI8Ke9bOq>bQB8}haoapMp zU8Pt<ns+osNIPU!3HQ--CB_n$Z@8NrJ5t79|E3)jc<(R5*kNBq7gey!oiAAl1q zEBMO%=<+e&Gi6`zB2Cy<;B%S0dW!Q!WJ8XtW%;$YZh&RKQA~A5p0iw23~qu?CI)4L zcy;RL0nsJCZ16flk_XsqJ^VQ&3wb+t)+Ylq?TVPVuu=tZw zzZT11h4@qxyXj=Dk*TKxs}t{_U~iCgnF1c>st>dYiI0ZPDDh&q; ziS&dGW2f%G7xNR`zV@vez5FBEh%cs`DcEujnmqGuP@JR(_pYg~jMVbyL^=r%6&=8m z&-quZq3JG26gG1{FWf7Tb1_0DwBh@Q%k{S&XH6KlM{Bt0A`#lN zUxE8$FN#ZIPrz5>tRM5NN)1>853u2Gw8`E(`s-v+Kfz}hw|=^B{gHEVK7iX!pF>pX zBgt;=lq>(-KokI6kg~b+_Avj~+u`D!nzx049>+9&@8ox%x(_Cl_6zKMbqdq$aoC{&&^O3DqXL+!ru3TVOMQ+gy`2^8UUyfE2 z>1jf@v%ytj8?cqpA>*wdJsGm-gjue|*Y2dBLTs^z-hS2Vs4sme5WHVKZesHHEOVRZ zxAiUSueYC|k3+@EH$RA zu(7c0J`?gfD@NzVAz^P-N81|Zr4(cX8)|lcd5s3WzBB%2`%m!lz9}UeWAS-%ve{*% z@_JwA1Y3J_hyk>R?Uk`r_N7t6wq{uciAv(##CdPToJK`=qzw_^s5=MM<`Befr^q)R z{X0t;@(Oi5K!&IA5xh>Urj$0H`6WO(QQicL;W?MF?QVf%lkcxPnmpWh=LX}gGzJ>-cg-z`N=1P3nCfgW6-<0G5z7L2z=u{5MZy!N?!^K_2p+pT{AT;>M8&{uSs<$4pD%*XWSv xbO&mYK&vV6&@cY?IYON_{{QEZTfe>!Z9h)k@Md^+1AOxV>1u;ERO?#k{{vv2v@ZYv literal 0 HcmV?d00001 diff --git a/front/dist/resources/objects/smallHandleFilledGrey.png b/front/dist/resources/objects/smallHandleFilledGrey.png new file mode 100644 index 0000000000000000000000000000000000000000..40e968fb816c3f3400ae00a0af985c3fdf42c75c GIT binary patch literal 3554 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 literal 0 HcmV?d00001 diff --git a/front/src/Phaser/Components/MobileJoystick.ts b/front/src/Phaser/Components/MobileJoystick.ts new file mode 100644 index 00000000..1ace529c --- /dev/null +++ b/front/src/Phaser/Components/MobileJoystick.ts @@ -0,0 +1,35 @@ +import VirtualJoystick from 'phaser3-rex-plugins/plugins/virtualjoystick.js'; + +const outOfScreenX = -1000; +const outOfScreenY = -1000; + + +//the assets were found here: https://hannemann.itch.io/virtual-joystick-pack-free +export const joystickBaseKey = 'joystickBase'; +export const joystickBaseImg = 'resources/objects/joystickSplitted.png'; +export const joystickThumbKey = 'joystickThumb'; +export const joystickThumbImg = 'resources/objects/smallHandleFilledGrey.png'; + +export class MobileJoystick extends VirtualJoystick { + + constructor(scene: Phaser.Scene) { + super(scene, { + x: outOfScreenX, + y: outOfScreenY, + radius: 20, + base: scene.add.image(0, 0, joystickBaseKey).setDisplaySize(60, 60).setDepth(99999), + thumb: scene.add.image(0, 0, joystickThumbKey).setDisplaySize(30, 30).setDepth(99999), + enable: true, + dir: "8dir", + }); + + this.scene.input.on('pointerdown', (pointer: { x: number; y: number; }) => { + this.x = pointer.x; + this.y = pointer.y; + }); + this.scene.input.on('pointerup', () => { + this.x = outOfScreenX; + this.y = outOfScreenY; + }); + } +} \ No newline at end of file diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 0f31a519..9c261cf2 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -72,6 +72,7 @@ import {worldFullMessageStream} from "../../Connexion/WorldFullMessageStream"; import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager"; import {touchScreenManager} from "../../Touch/TouchScreenManager"; import {PinchManager} from "../UserInput/PinchManager"; +import {joystickBaseImg, joystickBaseKey, joystickThumbImg, joystickThumbKey} from "../Components/MobileJoystick"; export interface GameSceneInitInterface { initPosition: PointInterface|null, @@ -189,6 +190,7 @@ export class GameScene extends ResizableScene implements CenterListener { //hook preload scene preload(): void { + addLoader(this); const localUser = localUserStore.getLocalUser(); const textures = localUser?.textures; if (textures) { @@ -198,6 +200,10 @@ export class GameScene extends ResizableScene implements CenterListener { } this.load.image(openChatIconName, 'resources/objects/talk.png'); + if (touchScreenManager.supportTouchScreen) { + this.load.image(joystickBaseKey, joystickBaseImg); + this.load.image(joystickThumbKey, joystickThumbImg); + } this.load.on(FILE_LOAD_ERROR, (file: {src: string}) => { // If we happen to be in HTTP and we are trying to load a URL in HTTPS only... (this happens only in dev environments) if (window.location.protocol === 'http:' && file.src === this.MapUrlFile && file.src.startsWith('http:') && this.originalMapUrl === undefined) { @@ -244,8 +250,6 @@ export class GameScene extends ResizableScene implements CenterListener { this.load.spritesheet('layout_modes', 'resources/objects/layout_modes.png', {frameWidth: 32, frameHeight: 32}); this.load.bitmapFont('main_font', 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml'); - - addLoader(this); } // FIXME: we need to put a "unknown" instead of a "any" and validate the structure of the JSON we are receiving. diff --git a/front/src/Phaser/UserInput/UserInputManager.ts b/front/src/Phaser/UserInput/UserInputManager.ts index 602afee7..2f14672b 100644 --- a/front/src/Phaser/UserInput/UserInputManager.ts +++ b/front/src/Phaser/UserInput/UserInputManager.ts @@ -1,7 +1,7 @@ -import { Direction, IVirtualJoystick } from "../../types"; +import { Direction } from "../../types"; import {GameScene} from "../Game/GameScene"; import {touchScreenManager} from "../../Touch/TouchScreenManager"; -import VirtualJoystick from 'phaser3-rex-plugins/plugins/virtualjoystick.js'; +import {MobileJoystick} from "../Components/MobileJoystick"; interface UserInputManagerDatum { keyInstance: Phaser.Input.Keyboard.Key; @@ -19,8 +19,6 @@ export enum UserInputEvent { JoystickMove, } -const outOfScreenX = -1000; -const outOfScreenY = -1000; //we cannot use a map structure so we have to create a replacment export class ActiveEventList { @@ -46,7 +44,7 @@ export class UserInputManager { private Scene: GameScene; private isInputDisabled : boolean; - private joystick!: IVirtualJoystick; + private joystick!: MobileJoystick; private joystickEvents = new ActiveEventList(); private joystickForceThreshold = 60; private joystickForceAccuX = 0; @@ -62,24 +60,7 @@ export class UserInputManager { } initVirtualJoystick() { - this.joystick = new VirtualJoystick(this, { - x: outOfScreenX, - y: outOfScreenY, - radius: 20, - base: this.Scene.add.circle(0, 0, 20, 0xdddddd), - thumb: this.Scene.add.circle(0, 0, 10, 0x000000), - enable: true, - dir: "8dir", - }); - - this.Scene.input.on('pointerdown', (pointer: { x: number; y: number; }) => { - this.joystick.x = pointer.x; - this.joystick.y = pointer.y; - }); - this.Scene.input.on('pointerup', (pointer: { x: number; y: number; }) => { - this.joystick.x = outOfScreenX; - this.joystick.y = outOfScreenY; - }); + this.joystick = new MobileJoystick(this.Scene); this.joystick.on("update", () => { this.joystickForceAccuX = this.joystick.forceX ? this.joystickForceAccuX : 0; this.joystickForceAccuY = this.joystick.forceY ? this.joystickForceAccuY : 0; From 2c862fe7e7ced311b198a19fceaf10e662ea7170 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 20 Apr 2021 18:49:42 +0200 Subject: [PATCH 46/66] Refactor Login Scene --- front/dist/resources/html/loginScene.html | 101 ++++++++++++++ front/dist/resources/objects/play_button.png | Bin 969 -> 0 bytes front/dist/resources/style/mobile-style.scss | 17 +++ front/src/Phaser/Login/LoginScene.ts | 135 ++++++++----------- 4 files changed, 176 insertions(+), 77 deletions(-) create mode 100644 front/dist/resources/html/loginScene.html delete mode 100644 front/dist/resources/objects/play_button.png create mode 100644 front/dist/resources/style/mobile-style.scss diff --git a/front/dist/resources/html/loginScene.html b/front/dist/resources/html/loginScene.html new file mode 100644 index 00000000..b237b3e2 --- /dev/null +++ b/front/dist/resources/html/loginScene.html @@ -0,0 +1,101 @@ + + + diff --git a/front/dist/resources/objects/play_button.png b/front/dist/resources/objects/play_button.png deleted file mode 100644 index 36aa309b6b26ddb120f8562bcb8c5090c8e8f38f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 969 zcmV;)12+7LP)jd6qRl{>LPf>_yuuR;D=wb2R1}h;Etm#RLFm zW z<#^dSuC!0nuyEc2=$TnB*O^|w`M&^eCNTb=0PFyGmYVr(JVp6lo_wU@n9iQMygZE! zGP)y75q)Qy6!v?%{wPS*YB}>&hILUeBzpk`vqo~8f{4qv@Y@`jnc_YlT?0AB-X_aqwF32)^Q7uS-xZh1*^ z&2g8NB)2^Eaf0MgpQSL#agwL$ktXJ^w5Msas>=QDHvqS5{KOfOTO_a54COa~2cEYZ zb^!bW@DjlH*s7#j%Too9Gx~7rKUYcalwD$mT;CjW?FxWP2d*6fa2CKE$*BST$;Y|` zV0ws9XMB4NGus8QO7c}MZeL>Yeh6*8RXZqHy^`|ZHP8?_LQzz&H*^zM%#MBGF`3%_|Qhf$~0+ana=>G+G<~- ziAxM+Hx4gi?|kYYKLLD{XbvBB4k>?xdQC>6=?1s3_h7z^uqefgewNt@)u1v>;+WYz rje-zD2qA div { + max-height: 240px; + } + + .sidebar > div { + max-height: 240px; + } + + .sidebar{ + max-width: 200px; + } + + .btn-cam-action div{ + bottom: 50px; + } +} \ No newline at end of file diff --git a/front/src/Phaser/Login/LoginScene.ts b/front/src/Phaser/Login/LoginScene.ts index 057cb6ae..ecffa6e5 100644 --- a/front/src/Phaser/Login/LoginScene.ts +++ b/front/src/Phaser/Login/LoginScene.ts @@ -1,30 +1,17 @@ import {gameManager} from "../Game/GameManager"; -import {TextField} from "../Components/TextField"; -import {TextInput} from "../Components/TextInput"; -import Image = Phaser.GameObjects.Image; import {SelectCharacterSceneName} from "./SelectCharacterScene"; import {ResizableScene} from "./ResizableScene"; -import {isUserNameValid, maxUserNameLength} from "../../Connexion/LocalUser"; import { localUserStore } from "../../Connexion/LocalUserStore"; -import Rectangle = Phaser.GameObjects.Rectangle; -import {touchScreenManager} from "../../Touch/TouchScreenManager"; -import {PinchManager} from "../UserInput/PinchManager"; +import {MenuScene} from "../Menu/MenuScene"; -//todo: put this constants in a dedicated file export const LoginSceneName = "LoginScene"; -enum LoginTextures { - icon = "icon", - mainFont = "main_font" -} + +const loginSceneKey = 'loginScene'; export class LoginScene extends ResizableScene { - private nameInput!: TextInput; - private textField!: TextField; - private infoTextField!: TextField; - private pressReturnField!: TextField; - private logo!: Image; + + private loginSceneElement!: Phaser.GameObjects.DOMElement; private name: string = ''; - private mobileTapRectangle!: Rectangle; constructor() { super({ @@ -34,81 +21,75 @@ export class LoginScene extends ResizableScene { } preload() { - //this.load.image(LoginTextures.playButton, "resources/objects/play_button.png"); - this.load.image(LoginTextures.icon, "resources/logos/tcm_full.png"); - // Note: arcade.png from the Phaser 3 examples at: https://github.com/photonstorm/phaser3-examples/tree/master/public/assets/fonts/bitmap - this.load.bitmapFont(LoginTextures.mainFont, 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml'); + this.load.html(loginSceneKey, 'resources/html/loginScene.html'); } create() { - if (touchScreenManager.supportTouchScreen) { - new PinchManager(this); - } + const middleX = this.getMiddleX(); + this.loginSceneElement = this.add.dom((middleX/2), 0).createFromCache(loginSceneKey); + MenuScene.revealMenusAfterInit(this.loginSceneElement, loginSceneKey); - this.nameInput = new TextInput(this, this.game.renderer.width / 2, 70, maxUserNameLength, this.name,(text: string) => { - this.name = text; - localUserStore.setName(text); - }) - .setInteractive() - .on('pointerdown', () => { - this.nameInput.focus(); - }) - - this.textField = new TextField(this, this.game.renderer.width / 2, 50, 'Enter your name:') - .setInteractive() - .on('pointerdown', () => { - this.nameInput.focus(); - }) - // For mobile purposes - we need a big enough touchable area. - this.mobileTapRectangle = this.add.rectangle( - this.game.renderer.width / 2, - 130, - this.game.renderer.width / 2, - 60, - ).setInteractive() - .on('pointerdown', () => { - this.login(this.name) - }) - this.pressReturnField = new TextField(this, this.game.renderer.width / 2, 130, 'Touch here\n\n or \n\nPress enter to start') - - this.logo = new Image(this, this.game.renderer.width - 30, this.game.renderer.height - 20, LoginTextures.icon); - this.add.existing(this.logo); - - const infoText = "Commands: \n - Arrows or W, A, S, D to move\n - SHIFT to run"; - this.infoTextField = new TextField(this, 10, this.game.renderer.height - 35, infoText, false); - - this.input.keyboard.on('keyup-ENTER', () => { - if (isUserNameValid(this.name)) { - this.login(this.name); + const pErrorElement = this.loginSceneElement.getChildByID('errorLoginScene') as HTMLInputElement; + const inputElement = this.loginSceneElement.getChildByID('loginSceneName') as HTMLInputElement; + inputElement.value = localUserStore.getName() ?? ''; + inputElement.focus(); + inputElement.addEventListener('keypress', (event: KeyboardEvent) => { + if(inputElement.value.length > 8){ + event.preventDefault(); + return; } + pErrorElement.innerHTML = ''; + if (event.key === 'Enter') { + event.preventDefault(); + this.login(inputElement); + return; + } + }); + + const continuingButton = this.loginSceneElement.getChildByID('loginSceneFormSubmit') as HTMLButtonElement; + continuingButton.addEventListener('click', (e) => { + e.preventDefault(); + this.login(inputElement); }); } - update(time: number, delta: number): void { - if (this.name == '') { - this.pressReturnField?.setVisible(false); - } else { - this.pressReturnField?.setVisible(!!(Math.floor(time / 500) % 2)); + private login(inputElement: HTMLInputElement): void { + const pErrorElement = this.loginSceneElement.getChildByID('errorLoginScene') as HTMLInputElement; + this.name = inputElement.value; + if (this.name === '') { + pErrorElement.innerHTML = 'The name is empty'; + return } - } - - private login(name: string): void { if (this.name === '') return - gameManager.setPlayerName(name); + gameManager.setPlayerName(this.name); this.scene.stop(LoginSceneName) gameManager.tryResumingGame(this, SelectCharacterSceneName); this.scene.remove(LoginSceneName) } - public onResize(ev: UIEvent): void { - this.textField.x = this.game.renderer.width / 2; - this.nameInput.setX(this.game.renderer.width / 2 - 64); - this.pressReturnField.x = this.game.renderer.width / 2; - this.mobileTapRectangle.x = this.game.renderer.width / 2; - this.logo.x = this.game.renderer.width - 30; - this.logo.y = this.game.renderer.height - 20; - this.infoTextField.y = this.game.renderer.height - 35; + update(time: number, delta: number): void { + const middleX = this.getMiddleX(); + this.tweens.add({ + targets: this.loginSceneElement, + x: (middleX/2), + duration: 1000, + ease: 'Power3' + }); } + public onResize(ev: UIEvent): void { + const middleX = this.getMiddleX(); + this.tweens.add({ + targets: this.loginSceneElement, + x: (middleX/2), + duration: 1000, + ease: 'Power3' + }); + } + + private getMiddleX() : number{ + const middleX = ((window.innerWidth) - ((this.loginSceneElement && this.loginSceneElement.width > 0 ? this.loginSceneElement.width : 200 /*FIXME to use a const will be injected in HTMLElement*/)*2)) / 2; + return (middleX > 0 ? middleX : 0); + } } From 09d5e0b689da3a707cbc327e5190871cbf4fedf5 Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 20 Apr 2021 18:58:19 +0200 Subject: [PATCH 47/66] Fix center select name modal --- front/dist/resources/html/loginScene.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/front/dist/resources/html/loginScene.html b/front/dist/resources/html/loginScene.html index b237b3e2..7011227d 100644 --- a/front/dist/resources/html/loginScene.html +++ b/front/dist/resources/html/loginScene.html @@ -4,7 +4,8 @@ border: 1px solid #42464b; border-radius: 6px; margin: 20px auto 0; - width: 200px; + width: 90%; + max-width: 200px; height: 200px; } #loginScene h1 { From 30c1825abd23e3d6a0050b5087dfa620348a4ebf Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 20 Apr 2021 19:54:37 +0200 Subject: [PATCH 48/66] Update syle mobile - update my video side - update user video side - update button video cam and microphone side --- front/dist/resources/style/index.scss | 3 +- front/dist/resources/style/mobile-style.scss | 53 ++++++++++++++++---- front/dist/resources/style/style.css | 18 ------- 3 files changed, 45 insertions(+), 29 deletions(-) diff --git a/front/dist/resources/style/index.scss b/front/dist/resources/style/index.scss index 4d0355b7..7454b991 100644 --- a/front/dist/resources/style/index.scss +++ b/front/dist/resources/style/index.scss @@ -1,2 +1,3 @@ @import "cowebsite.scss"; -@import "style.css"; \ No newline at end of file +@import "style.css"; +@import "mobile-style.scss"; \ No newline at end of file diff --git a/front/dist/resources/style/mobile-style.scss b/front/dist/resources/style/mobile-style.scss index 286735b4..329dc78d 100644 --- a/front/dist/resources/style/mobile-style.scss +++ b/front/dist/resources/style/mobile-style.scss @@ -1,17 +1,50 @@ -@media only screen and (max-width: 600px) { - .sidebar > div { - max-height: 240px; +@media only screen and (max-width: 700px) { + video#myCamVideo { + width: 200px; } - .sidebar > div { - max-height: 240px; + .sidebar { + width: 20%; + min-width: 200px; + position: absolute; + display: block; + right: 0; + height: 80%; + + &> div { + max-height: 120px; + min-width: 200px; + } + + .video-container{ + min-width: 200px; + } } - .sidebar{ - max-width: 200px; + .btn-cam-action div { + bottom: 30px; + + &.btn-micro { + right: 15px; + } + + &.btn-monitor { + right: 171px; + } + + &.btn-video { + right: 95px; + } } - .btn-cam-action div{ - bottom: 50px; + .main-section { + position: absolute; + width: 100%; + min-width: 400px; + + & > div { + z-index: 2; + } } -} \ No newline at end of file +} + diff --git a/front/dist/resources/style/style.css b/front/dist/resources/style/style.css index ff79245c..492f5fa3 100644 --- a/front/dist/resources/style/style.css +++ b/front/dist/resources/style/style.css @@ -338,24 +338,6 @@ body { } -@media (max-aspect-ratio: 1/1) { - .game-overlay { - flex-direction: column; - } - - .sidebar { - flex-direction: row; - align-items: flex-end; - } - - .sidebar > div { - max-width: 21%; - } - - .sidebar > div:hover { - max-width: 25%; - } -} #game { width: 100%; From 20e0a2553e9319877ecd9ea9b5adecb219b0141b Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Tue, 20 Apr 2021 21:11:05 +0200 Subject: [PATCH 49/66] Update cowebsite mobile style --- front/dist/resources/style/cowebsite.scss | 48 ------------ front/dist/resources/style/mobile-style.scss | 80 +++++++++++++++++--- 2 files changed, 69 insertions(+), 59 deletions(-) diff --git a/front/dist/resources/style/cowebsite.scss b/front/dist/resources/style/cowebsite.scss index b752728d..515dc0df 100644 --- a/front/dist/resources/style/cowebsite.scss +++ b/front/dist/resources/style/cowebsite.scss @@ -87,51 +87,3 @@ } } } -@media (max-aspect-ratio: 1/1) { - - #main-container { - display: flex; - flex-direction: column-reverse; - } - - - #cowebsite { - left: 0; - top: 0; - width: 100%; - height: 50%; - display: flex; - flex-direction: column-reverse; - - &.loading { - transform: translateY(-90%); - } - &.hidden { - transform: translateY(-100%); - } - - main { - height: 100%; - } - - - aside { - height: 30px; - cursor: ns-resize; - flex-direction: column; - - img { - cursor: ns-resize; - } - } - - .top-right-btn { - &#cowebsite-close { - right: 0px; - } - &#cowebsite-fullscreen { - right: 25px; - } - } - } -} \ No newline at end of file diff --git a/front/dist/resources/style/mobile-style.scss b/front/dist/resources/style/mobile-style.scss index 329dc78d..452e1604 100644 --- a/front/dist/resources/style/mobile-style.scss +++ b/front/dist/resources/style/mobile-style.scss @@ -1,6 +1,6 @@ @media only screen and (max-width: 700px) { video#myCamVideo { - width: 200px; + width: 150px; } .sidebar { @@ -21,19 +21,28 @@ } } - .btn-cam-action div { - bottom: 30px; - - &.btn-micro { - right: 15px; + .btn-cam-action { + &:hover{ + transform: translateY(20px); } + div { + &:hover { + background-color: #666; + } - &.btn-monitor { - right: 171px; - } + bottom: 30px; - &.btn-video { - right: 95px; + &.btn-micro { + right: 0; + } + + &.btn-monitor { + right: 130px; + } + + &.btn-video { + right: 65px; + } } } @@ -48,3 +57,52 @@ } } +@media (max-aspect-ratio: 1/1) { + + #main-container { + display: flex; + flex-direction: column-reverse; + } + + + #cowebsite { + left: 0; + top: 0; + width: 100%; + height: 50%; + display: flex; + flex-direction: column-reverse; + + &.loading { + transform: translateY(-90%); + } + &.hidden { + transform: translateY(-100%); + } + + main { + height: 100%; + } + + + aside { + height: 30px; + cursor: ns-resize; + flex-direction: column; + + img { + cursor: ns-resize; + } + } + + .top-right-btn { + &#cowebsite-close { + right: 0; + } + &#cowebsite-fullscreen { + left: 0; + } + } + } +} + From 78d888ffaf9ba95d905a23776d202088db403c6e Mon Sep 17 00:00:00 2001 From: Gregoire Parant Date: Wed, 21 Apr 2021 00:53:45 +0200 Subject: [PATCH 50/66] Update menu mobile - Add mobile style share modal - Add mobile style settings modal - Add menu mobile style --- front/dist/resources/html/gameMenu.html | 18 ++++++ front/dist/resources/html/gameMenuIcon.html | 8 ++- .../dist/resources/html/gameQualityMenu.html | 8 +-- front/dist/resources/html/gameShare.html | 8 +-- .../resources/html/helpCameraSettings.html | 20 ++----- front/dist/resources/html/loginScene.html | 23 +++++--- .../Phaser/Menu/HelpCameraSettingsScene.ts | 57 ++++++++++++++++--- front/src/Phaser/Menu/MenuScene.ts | 2 +- 8 files changed, 102 insertions(+), 42 deletions(-) diff --git a/front/dist/resources/html/gameMenu.html b/front/dist/resources/html/gameMenu.html index ce740ec5..399cf349 100644 --- a/front/dist/resources/html/gameMenu.html +++ b/front/dist/resources/html/gameMenu.html @@ -1,4 +1,7 @@