1}>
2}>
3}>
diff --git a/front/src/Components/UI/AudioPlaying.svelte b/front/src/Components/UI/AudioPlaying.svelte
new file mode 100644
index 00000000..8889ac52
--- /dev/null
+++ b/front/src/Components/UI/AudioPlaying.svelte
@@ -0,0 +1,52 @@
+
+
+
+
+
Audio message
+
+
+
+
+
+
diff --git a/front/src/Components/UI/images/megaphone.svg b/front/src/Components/UI/images/megaphone.svg
new file mode 100644
index 00000000..708f860c
--- /dev/null
+++ b/front/src/Components/UI/images/megaphone.svg
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/front/src/Components/images/logo.png b/front/src/Components/images/logo.png
new file mode 100644
index 00000000..f4440ad5
Binary files /dev/null and b/front/src/Components/images/logo.png differ
diff --git a/front/src/Components/selectCharacter/SelectCharacterScene.svelte b/front/src/Components/selectCharacter/SelectCharacterScene.svelte
new file mode 100644
index 00000000..e227771c
--- /dev/null
+++ b/front/src/Components/selectCharacter/SelectCharacterScene.svelte
@@ -0,0 +1,92 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/front/src/Connexion/ConnectionManager.ts b/front/src/Connexion/ConnectionManager.ts
index 7bac4ba0..e09547f3 100644
--- a/front/src/Connexion/ConnectionManager.ts
+++ b/front/src/Connexion/ConnectionManager.ts
@@ -4,7 +4,7 @@ import {RoomConnection} from "./RoomConnection";
import type {OnConnectInterface, PositionInterface, ViewportInterface} from "./ConnexionModels";
import {GameConnexionTypes, urlManager} from "../Url/UrlManager";
import {localUserStore} from "./LocalUserStore";
-import {LocalUser} from "./LocalUser";
+import {CharacterTexture, LocalUser} from "./LocalUser";
import {Room} from "./Room";
@@ -46,8 +46,8 @@ class ConnectionManager {
urlManager.pushRoomIdToUrl(room);
return Promise.resolve(room);
} else if (connexionType === GameConnexionTypes.organization || connexionType === GameConnexionTypes.anonymous || connexionType === GameConnexionTypes.empty) {
- const localUser = localUserStore.getLocalUser();
+ let localUser = localUserStore.getLocalUser();
if (localUser && localUser.jwtToken && localUser.uuid && localUser.textures) {
this.localUser = localUser;
try {
@@ -57,16 +57,42 @@ class ConnectionManager {
console.error('JWT token invalid. Did it expire? Login anonymously instead.');
await this.anonymousLogin();
}
- } else {
+ }else{
await this.anonymousLogin();
}
- let roomId: string
+
+ localUser = localUserStore.getLocalUser();
+ if(!localUser){
+ throw "Error to store local user data";
+ }
+
+ let roomId: string;
if (connexionType === GameConnexionTypes.empty) {
roomId = START_ROOM_URL;
} else {
roomId = window.location.pathname + window.location.search + window.location.hash;
}
- return Promise.resolve(new Room(roomId));
+
+ //get detail map for anonymous login and set texture in local storage
+ const room = new Room(roomId);
+ const mapDetail = await room.getMapDetail();
+ if(mapDetail.textures != undefined && mapDetail.textures.length > 0) {
+ //check if texture was changed
+ if(localUser.textures.length === 0){
+ localUser.textures = mapDetail.textures;
+ }else{
+ mapDetail.textures.forEach((newTexture) => {
+ const alreadyExistTexture = localUser?.textures.find((c) => newTexture.id === c.id);
+ if(localUser?.textures.findIndex((c) => newTexture.id === c.id) !== -1){
+ return;
+ }
+ localUser?.textures.push(newTexture)
+ });
+ }
+ this.localUser = localUser;
+ localUserStore.saveUser(localUser);
+ }
+ return Promise.resolve(room);
}
return Promise.reject(new Error('Invalid URL'));
diff --git a/front/src/Connexion/LocalUser.ts b/front/src/Connexion/LocalUser.ts
index 43b184cf..c877d119 100644
--- a/front/src/Connexion/LocalUser.ts
+++ b/front/src/Connexion/LocalUser.ts
@@ -10,7 +10,7 @@ export interface CharacterTexture {
export const maxUserNameLength: number = MAX_USERNAME_LENGTH;
export function isUserNameValid(value: unknown): boolean {
- return typeof value === "string" && value.length > 0 && value.length < maxUserNameLength && value.indexOf(' ') === -1;
+ return typeof value === "string" && value.length > 0 && value.length <= maxUserNameLength && value.indexOf(' ') === -1;
}
export function areCharacterLayersValid(value: string[] | null): boolean {
@@ -24,6 +24,6 @@ export function areCharacterLayersValid(value: string[] | null): boolean {
}
export class LocalUser {
- constructor(public readonly uuid:string, public readonly jwtToken: string, public readonly textures: CharacterTexture[]) {
+ constructor(public readonly uuid:string, public readonly jwtToken: string, public textures: CharacterTexture[]) {
}
}
diff --git a/front/src/Connexion/Room.ts b/front/src/Connexion/Room.ts
index 05d94440..3ae8d2ed 100644
--- a/front/src/Connexion/Room.ts
+++ b/front/src/Connexion/Room.ts
@@ -1,10 +1,17 @@
import Axios from "axios";
import {PUSHER_URL} from "../Enum/EnvironmentVariable";
+import type {CharacterTexture} from "./LocalUser";
+
+export class MapDetail{
+ constructor(public readonly mapUrl: string, public readonly textures : CharacterTexture[]|undefined) {
+ }
+}
export class Room {
public readonly id: string;
public readonly isPublic: boolean;
private mapUrl: string|undefined;
+ private textures: CharacterTexture[]|undefined;
private instance: string|undefined;
private _search: URLSearchParams;
@@ -50,10 +57,10 @@ export class Room {
return {roomId, hash}
}
- public async getMapUrl(): Promise
{
- return new Promise((resolve, reject) => {
- if (this.mapUrl !== undefined) {
- resolve(this.mapUrl);
+ public async getMapDetail(): Promise {
+ return new Promise((resolve, reject) => {
+ if (this.mapUrl !== undefined && this.textures != undefined) {
+ resolve(new MapDetail(this.mapUrl, this.textures));
return;
}
@@ -61,7 +68,7 @@ export class Room {
const match = /_\/[^/]+\/(.+)/.exec(this.id);
if (!match) throw new Error('Could not extract url from "'+this.id+'"');
this.mapUrl = window.location.protocol+'//'+match[1];
- resolve(this.mapUrl);
+ resolve(new MapDetail(this.mapUrl, this.textures));
return;
} else {
// We have a private ID, we need to query the map URL from the server.
@@ -71,7 +78,7 @@ export class Room {
params: urlParts
}).then(({data}) => {
console.log('Map ', this.id, ' resolves to URL ', data.mapUrl);
- resolve(data.mapUrl);
+ resolve(data);
return;
}).catch((reason) => {
reject(reason);
diff --git a/front/src/Enum/EnvironmentVariable.ts b/front/src/Enum/EnvironmentVariable.ts
index 47fe0a30..73f6427c 100644
--- a/front/src/Enum/EnvironmentVariable.ts
+++ b/front/src/Enum/EnvironmentVariable.ts
@@ -12,8 +12,9 @@ const JITSI_URL : string|undefined = (process.env.JITSI_URL === '') ? undefined
const JITSI_PRIVATE_MODE : boolean = process.env.JITSI_PRIVATE_MODE == "true";
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
-const MAX_USERNAME_LENGTH = parseInt(process.env.MAX_USERNAME_LENGTH || '8');
-const MAX_PER_GROUP = parseInt(process.env.MAX_PER_GROUP || '4');
+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 const DISPLAY_TERMS_OF_USE = process.env.DISPLAY_TERMS_OF_USE == 'true';
export const isMobile = ():boolean => ( ( window.innerWidth <= 800 ) || ( window.innerHeight <= 600 ) );
@@ -31,7 +32,5 @@ export {
TURN_USER,
TURN_PASSWORD,
JITSI_URL,
- JITSI_PRIVATE_MODE,
- MAX_USERNAME_LENGTH,
- MAX_PER_GROUP
+ JITSI_PRIVATE_MODE
}
diff --git a/front/src/Phaser/Components/MobileJoystick.ts b/front/src/Phaser/Components/MobileJoystick.ts
index 46efcbc2..b3fc021b 100644
--- a/front/src/Phaser/Components/MobileJoystick.ts
+++ b/front/src/Phaser/Components/MobileJoystick.ts
@@ -28,13 +28,13 @@ export class MobileJoystick extends VirtualJoystick {
this.visible = false;
this.enable = false;
- this.scene.input.on('pointerdown', (pointer: { x: number; y: number; wasTouch: boolean; event: TouchEvent }) => {
+ this.scene.input.on('pointerdown', (pointer: Phaser.Input.Pointer) => {
if (!pointer.wasTouch) {
return;
}
// Let's only display the joystick if there is one finger on the screen
- if (pointer.event.touches.length === 1) {
+ if ((pointer.event as TouchEvent).touches.length === 1) {
this.x = pointer.x;
this.y = pointer.y;
this.visible = true;
diff --git a/front/src/Phaser/Components/SoundMeter.ts b/front/src/Phaser/Components/SoundMeter.ts
index 1d6f7eba..53802d31 100644
--- a/front/src/Phaser/Components/SoundMeter.ts
+++ b/front/src/Phaser/Components/SoundMeter.ts
@@ -1,3 +1,5 @@
+import type {IAnalyserNode, IAudioContext, IMediaStreamAudioSourceNode} from 'standardized-audio-context';
+
/**
* Class to measure the sound volume of a media stream
*/
@@ -5,10 +7,10 @@ export class SoundMeter {
private instant: number;
private clip: number;
//private script: ScriptProcessorNode;
- private analyser: AnalyserNode|undefined;
+ private analyser: IAnalyserNode|undefined;
private dataArray: Uint8Array|undefined;
- private context: AudioContext|undefined;
- private source: MediaStreamAudioSourceNode|undefined;
+ private context: IAudioContext|undefined;
+ private source: IMediaStreamAudioSourceNode|undefined;
constructor() {
this.instant = 0.0;
@@ -16,7 +18,7 @@ export class SoundMeter {
//this.script = context.createScriptProcessor(2048, 1, 1);
}
- private init(context: AudioContext) {
+ private init(context: IAudioContext) {
this.context = context;
this.analyser = this.context.createAnalyser();
@@ -25,8 +27,12 @@ export class SoundMeter {
this.dataArray = new Uint8Array(bufferLength);
}
- public connectToSource(stream: MediaStream, context: AudioContext): void
+ public connectToSource(stream: MediaStream, context: IAudioContext): void
{
+ if (this.source !== undefined) {
+ this.stop();
+ }
+
this.init(context);
this.source = this.context?.createMediaStreamSource(stream);
@@ -81,56 +87,3 @@ export class SoundMeter {
}
-
-// Meter class that generates a number correlated to audio volume.
-// The meter class itself displays nothing, but it makes the
-// instantaneous and time-decaying volumes available for inspection.
-// It also reports on the fraction of samples that were at or near
-// the top of the measurement range.
-/*function SoundMeter(context) {
- this.context = context;
- this.instant = 0.0;
- this.slow = 0.0;
- this.clip = 0.0;
- this.script = context.createScriptProcessor(2048, 1, 1);
- const that = this;
- this.script.onaudioprocess = function(event) {
- const input = event.inputBuffer.getChannelData(0);
- let i;
- let sum = 0.0;
- let clipcount = 0;
- for (i = 0; i < input.length; ++i) {
- sum += input[i] * input[i];
- if (Math.abs(input[i]) > 0.99) {
- clipcount += 1;
- }
- }
- that.instant = Math.sqrt(sum / input.length);
- that.slow = 0.95 * that.slow + 0.05 * that.instant;
- that.clip = clipcount / input.length;
- };
-}
-
-SoundMeter.prototype.connectToSource = function(stream, callback) {
- console.log('SoundMeter connecting');
- try {
- this.mic = this.context.createMediaStreamSource(stream);
- this.mic.connect(this.script);
- // necessary to make sample run, but should not be.
- this.script.connect(this.context.destination);
- if (typeof callback !== 'undefined') {
- callback(null);
- }
- } catch (e) {
- console.error(e);
- if (typeof callback !== 'undefined') {
- callback(e);
- }
- }
-};
-
-SoundMeter.prototype.stop = function() {
- this.mic.disconnect();
- this.script.disconnect();
-};
-*/
diff --git a/front/src/Phaser/Components/SoundMeterSprite.ts b/front/src/Phaser/Components/SoundMeterSprite.ts
deleted file mode 100644
index 582617f9..00000000
--- a/front/src/Phaser/Components/SoundMeterSprite.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import Container = Phaser.GameObjects.Container;
-import type {Scene} from "phaser";
-import GameObject = Phaser.GameObjects.GameObject;
-import Rectangle = Phaser.GameObjects.Rectangle;
-
-
-export class SoundMeterSprite extends Container {
- private rectangles: Rectangle[] = new Array();
- private static readonly NB_BARS = 20;
-
- constructor(scene: Scene, x?: number, y?: number, children?: GameObject[]) {
- super(scene, x, y, children);
-
- for (let i = 0; i < SoundMeterSprite.NB_BARS; i++) {
- const rectangle = new Rectangle(scene, i * 13, 0, 10, 20, (Math.round(255 - i * 255 / SoundMeterSprite.NB_BARS) << 8) + (Math.round(i * 255 / SoundMeterSprite.NB_BARS) << 16));
- this.add(rectangle);
- this.rectangles.push(rectangle);
- }
- }
-
- /**
- * A number between 0 and 100
- *
- * @param volume
- */
- public setVolume(volume: number): void {
-
- const normalizedVolume = volume / 100 * SoundMeterSprite.NB_BARS;
- for (let i = 0; i < SoundMeterSprite.NB_BARS; i++) {
- if (normalizedVolume < i) {
- this.rectangles[i].alpha = 0.5;
- } else {
- this.rectangles[i].alpha = 1;
- }
- }
- }
-
- public getWidth(): number {
- return SoundMeterSprite.NB_BARS * 13;
- }
-
-
-
-}
diff --git a/front/src/Phaser/Game/DirtyScene.ts b/front/src/Phaser/Game/DirtyScene.ts
index 2e94aa66..3e1f3cdf 100644
--- a/front/src/Phaser/Game/DirtyScene.ts
+++ b/front/src/Phaser/Game/DirtyScene.ts
@@ -69,7 +69,7 @@ export abstract class DirtyScene extends ResizableScene {
return this.dirty || this.objectListChanged;
}
- public onResize(ev: UIEvent): void {
+ public onResize(): void {
this.objectListChanged = true;
}
}
diff --git a/front/src/Phaser/Game/Game.ts b/front/src/Phaser/Game/Game.ts
index 01aecf9f..e267e80a 100644
--- a/front/src/Phaser/Game/Game.ts
+++ b/front/src/Phaser/Game/Game.ts
@@ -21,14 +21,22 @@ export class Game extends Phaser.Game {
constructor(GameConfig: Phaser.Types.Core.GameConfig) {
super(GameConfig);
- window.addEventListener('resize', (event) => {
+ this.scale.on(Phaser.Scale.Events.RESIZE, () => {
+ for (const scene of this.scene.getScenes(true)) {
+ if (scene instanceof ResizableScene) {
+ scene.onResize();
+ }
+ }
+ })
+
+ /*window.addEventListener('resize', (event) => {
// Let's trigger the onResize method of any active scene that is a ResizableScene
for (const scene of this.scene.getScenes(true)) {
if (scene instanceof ResizableScene) {
scene.onResize(event);
}
}
- });
+ });*/
}
public step(time: number, delta: number)
diff --git a/front/src/Phaser/Game/GameManager.ts b/front/src/Phaser/Game/GameManager.ts
index 2a1d3d8a..cd2575c0 100644
--- a/front/src/Phaser/Game/GameManager.ts
+++ b/front/src/Phaser/Game/GameManager.ts
@@ -2,11 +2,13 @@ import {GameScene} from "./GameScene";
import {connectionManager} from "../../Connexion/ConnectionManager";
import type {Room} from "../../Connexion/Room";
import {MenuScene, MenuSceneName} from "../Menu/MenuScene";
-import {HelpCameraSettingsScene, HelpCameraSettingsSceneName} from "../Menu/HelpCameraSettingsScene";
import {LoginSceneName} from "../Login/LoginScene";
import {SelectCharacterSceneName} from "../Login/SelectCharacterScene";
import {EnableCameraSceneName} from "../Login/EnableCameraScene";
import {localUserStore} from "../../Connexion/LocalUserStore";
+import {get} from "svelte/store";
+import {requestedCameraState, requestedMicrophoneState} from "../../Stores/MediaStore";
+import {helpCameraSettingsVisibleStore} from "../../Stores/HelpCameraSettingsStore";
export interface HasMovedEvent {
direction: string;
@@ -76,11 +78,11 @@ export class GameManager {
public async loadMap(room: Room, scenePlugin: Phaser.Scenes.ScenePlugin): Promise {
const roomID = room.id;
- const mapUrl = await room.getMapUrl();
+ const mapDetail = await room.getMapDetail();
const gameIndex = scenePlugin.getIndex(roomID);
if(gameIndex === -1){
- const game : Phaser.Scene = new GameScene(room, mapUrl);
+ const game : Phaser.Scene = new GameScene(room, mapDetail.mapUrl);
scenePlugin.add(roomID, game, false);
}
}
@@ -89,7 +91,11 @@ export class GameManager {
console.log('starting '+ (this.currentGameSceneName || this.startRoom.id))
scenePlugin.start(this.currentGameSceneName || this.startRoom.id);
scenePlugin.launch(MenuSceneName);
- scenePlugin.launch(HelpCameraSettingsSceneName);//700
+
+ if(!localUserStore.getHelpCameraSettingsShown() && (!get(requestedMicrophoneState) || !get(requestedCameraState))){
+ helpCameraSettingsVisibleStore.set(true);
+ localUserStore.setHelpCameraSettingsShown();
+ }
}
public gameSceneIsCreated(scene: GameScene) {
diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts
index d227a7f0..f16ee988 100644
--- a/front/src/Phaser/Game/GameScene.ts
+++ b/front/src/Phaser/Game/GameScene.ts
@@ -253,11 +253,6 @@ export class GameScene extends DirtyScene implements CenterListener {
this.load.image(joystickBaseKey, joystickBaseImg);
this.load.image(joystickThumbKey, joystickThumbImg);
}
- //todo: in an emote manager.
- this.load.spritesheet('emote-music', 'resources/emotes/pipo-popupemotes005.png', {
- frameHeight: 32,
- frameWidth: 32,
- });
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) {
@@ -306,7 +301,7 @@ export class GameScene extends DirtyScene 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');
//eslint-disable-next-line @typescript-eslint/no-explicit-any
- (this.load as any).rexWebFont({
+ (this.load as any).rexWebFont({
custom: {
families: ['Press Start 2P'],
urls: ['/resources/fonts/fonts.css'],
@@ -1052,6 +1047,8 @@ ${escapedMessage}
this.pinchManager?.destroy();
this.emoteManager.destroy();
+ mediaManager.hideGameOverlay();
+
for(const iframeEvents of this.iframeSubscriptionList){
iframeEvents.unsubscribe();
}
@@ -1252,7 +1249,10 @@ ${escapedMessage}
this.companion,
this.companion !== null ? lazyLoadCompanionResource(this.load, this.companion) : undefined
);
- this.CurrentPlayer.on('pointerdown', () => {
+ this.CurrentPlayer.on('pointerdown', (pointer: Phaser.Input.Pointer) => {
+ if (pointer.wasTouch && (pointer.event as TouchEvent).touches.length > 1) {
+ return; //we don't want the menu to open when pinching on a touch screen.
+ }
this.emoteManager.getMenuImages().then((emoteMenuElements) => this.CurrentPlayer.openOrCloseEmoteMenu(emoteMenuElements))
})
this.CurrentPlayer.on(requestEmoteEventName, (emoteKey: string) => {
@@ -1558,8 +1558,8 @@ ${escapedMessage}
this.connection?.emitActionableEvent(itemId, eventName, state, parameters);
}
- public onResize(ev: UIEvent): void {
- super.onResize(ev);
+ public onResize(): void {
+ super.onResize();
this.reposition();
// Send new viewport to server
diff --git a/front/src/Phaser/Game/SoundManager.ts b/front/src/Phaser/Game/SoundManager.ts
index f0210494..47614fca 100644
--- a/front/src/Phaser/Game/SoundManager.ts
+++ b/front/src/Phaser/Game/SoundManager.ts
@@ -17,7 +17,9 @@ class SoundManager {
return res(sound);
}
loadPlugin.audio(soundUrl, soundUrl);
- loadPlugin.once('filecomplete-audio-' + soundUrl, () => res(soundManager.add(soundUrl)));
+ loadPlugin.once('filecomplete-audio-' + soundUrl, () => {
+ res(soundManager.add(soundUrl));
+ });
loadPlugin.start();
});
this.soundPromises.set(soundUrl,soundPromise);
diff --git a/front/src/Phaser/Login/CustomizeScene.ts b/front/src/Phaser/Login/CustomizeScene.ts
index 8b9a9a7a..3d85cdd5 100644
--- a/front/src/Phaser/Login/CustomizeScene.ts
+++ b/front/src/Phaser/Login/CustomizeScene.ts
@@ -11,6 +11,10 @@ import {AbstractCharacterScene} from "./AbstractCharacterScene";
import {areCharacterLayersValid} from "../../Connexion/LocalUser";
import { MenuScene } from "../Menu/MenuScene";
import { SelectCharacterSceneName } from "./SelectCharacterScene";
+import {customCharacterSceneVisibleStore} from "../../Stores/CustomCharacterStore";
+import {selectCharacterSceneVisibleStore} from "../../Stores/SelectCharacterStore";
+import {waScaleManager} from "../Services/WaScaleManager";
+import {isMobile} from "../../Enum/EnvironmentVariable";
export const CustomizeSceneName = "CustomizeScene";
@@ -22,10 +26,10 @@ export class CustomizeScene extends AbstractCharacterScene {
private selectedLayers: number[] = [0];
private containersRow: Container[][] = [];
- private activeRow:number = 0;
+ public activeRow:number = 0;
private layers: BodyResourceDescriptionInterface[][] = [];
- private customizeSceneElement!: Phaser.GameObjects.DOMElement;
+ protected lazyloadingAttempt = true; //permit to update texture loaded after renderer
constructor() {
super({
@@ -36,7 +40,6 @@ export class CustomizeScene extends AbstractCharacterScene {
preload() {
this.load.html(customizeSceneKey, 'resources/html/CustomCharacterScene.html');
- this.layers = loadAllLayers(this.load);
this.loadCustomSceneSelectCharacters().then((bodyResourceDescriptions) => {
bodyResourceDescriptions.forEach((bodyResourceDescription) => {
if(bodyResourceDescription.level == undefined || bodyResourceDescription.level < 0 || bodyResourceDescription.level > 5 ){
@@ -44,43 +47,28 @@ export class CustomizeScene extends AbstractCharacterScene {
}
this.layers[bodyResourceDescription.level].unshift(bodyResourceDescription);
});
+ this.lazyloadingAttempt = true;
});
+ this.layers = loadAllLayers(this.load);
+ this.lazyloadingAttempt = false;
+
+
//this function must stay at the end of preload function
addLoader(this);
}
create() {
- this.customizeSceneElement = this.add.dom(-1000, 0).createFromCache(customizeSceneKey);
- this.centerXDomElement(this.customizeSceneElement, 150);
- MenuScene.revealMenusAfterInit(this.customizeSceneElement, customizeSceneKey);
-
- this.customizeSceneElement.addListener('click');
- this.customizeSceneElement.on('click', (event:MouseEvent) => {
- event.preventDefault();
- if((event?.target as HTMLInputElement).id === 'customizeSceneButtonLeft') {
- this.moveCursorHorizontally(-1);
- }else if((event?.target as HTMLInputElement).id === 'customizeSceneButtonRight') {
- this.moveCursorHorizontally(1);
- }else if((event?.target as HTMLInputElement).id === 'customizeSceneButtonDown') {
- this.moveCursorVertically(1);
- }else if((event?.target as HTMLInputElement).id === 'customizeSceneButtonUp') {
- this.moveCursorVertically(-1);
- }else if((event?.target as HTMLInputElement).id === 'customizeSceneFormBack') {
- if(this.activeRow > 0){
- this.moveCursorVertically(-1);
- }else{
- this.backToPreviousScene();
- }
- }else if((event?.target as HTMLButtonElement).id === 'customizeSceneFormSubmit') {
- if(this.activeRow < 5){
- this.moveCursorVertically(1);
- }else{
- this.nextSceneToCamera();
- }
- }
+ customCharacterSceneVisibleStore.set(true);
+ this.events.addListener('wake', () => {
+ waScaleManager.saveZoom();
+ waScaleManager.zoomModifier = isMobile() ? 3 : 1;
+ customCharacterSceneVisibleStore.set(true);
});
+ waScaleManager.saveZoom();
+ waScaleManager.zoomModifier = isMobile() ? 3 : 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 / 3, 32, 33)
this.Rectangle.setStrokeStyle(2, 0xFFFFFF);
this.add.existing(this.Rectangle);
@@ -116,7 +104,7 @@ export class CustomizeScene extends AbstractCharacterScene {
this.onResize();
}
- private moveCursorHorizontally(index: number): void {
+ public moveCursorHorizontally(index: number): void {
this.selectedLayers[this.activeRow] += index;
if (this.selectedLayers[this.activeRow] < 0) {
this.selectedLayers[this.activeRow] = 0
@@ -128,27 +116,7 @@ export class CustomizeScene extends AbstractCharacterScene {
this.saveInLocalStorage();
}
- private moveCursorVertically(index:number): void {
-
- if(index === -1 && this.activeRow === 5){
- const button = this.customizeSceneElement.getChildByID('customizeSceneFormSubmit') as HTMLButtonElement;
- button.innerHTML = `Next `;
- }
-
- if(index === 1 && this.activeRow === 4){
- const button = this.customizeSceneElement.getChildByID('customizeSceneFormSubmit') as HTMLButtonElement;
- button.innerText = 'Finish';
- }
-
- if(index === -1 && this.activeRow === 1){
- const button = this.customizeSceneElement.getChildByID('customizeSceneFormBack') as HTMLButtonElement;
- button.innerText = `Return`;
- }
-
- if(index === 1 && this.activeRow === 0){
- const button = this.customizeSceneElement.getChildByID('customizeSceneFormBack') as HTMLButtonElement;
- button.innerHTML = `Back `;
- }
+ public moveCursorVertically(index:number): void {
this.activeRow += index;
if (this.activeRow < 0) {
@@ -262,6 +230,10 @@ export class CustomizeScene extends AbstractCharacterScene {
update(time: number, delta: number): void {
+ if(this.lazyloadingAttempt){
+ this.moveLayers();
+ this.lazyloadingAttempt = false;
+ }
}
public onResize(): void {
@@ -269,8 +241,6 @@ export class CustomizeScene extends AbstractCharacterScene {
this.Rectangle.x = this.cameras.main.worldView.x + this.cameras.main.width / 2;
this.Rectangle.y = this.cameras.main.worldView.y + this.cameras.main.height / 3;
-
- this.centerXDomElement(this.customizeSceneElement, 150);
}
private nextSceneToCamera(){
@@ -288,12 +258,16 @@ export class CustomizeScene extends AbstractCharacterScene {
gameManager.setCharacterLayers(layers);
this.scene.sleep(CustomizeSceneName);
- this.scene.remove(SelectCharacterSceneName);
+ waScaleManager.restoreZoom();
+ this.events.removeListener('wake');
gameManager.tryResumingGame(this, EnableCameraSceneName);
+ customCharacterSceneVisibleStore.set(false);
}
private backToPreviousScene(){
this.scene.sleep(CustomizeSceneName);
+ waScaleManager.restoreZoom();
this.scene.run(SelectCharacterSceneName);
+ customCharacterSceneVisibleStore.set(false);
}
}
diff --git a/front/src/Phaser/Login/EnableCameraScene.ts b/front/src/Phaser/Login/EnableCameraScene.ts
index 6002da7b..ba27cd07 100644
--- a/front/src/Phaser/Login/EnableCameraScene.ts
+++ b/front/src/Phaser/Login/EnableCameraScene.ts
@@ -3,7 +3,6 @@ import {TextField} from "../Components/TextField";
import Image = Phaser.GameObjects.Image;
import {mediaManager} from "../../WebRtc/MediaManager";
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";
@@ -11,304 +10,41 @@ import Zone = Phaser.GameObjects.Zone;
import { MenuScene } from "../Menu/MenuScene";
import {ResizableScene} from "./ResizableScene";
import {
- audioConstraintStore,
enableCameraSceneVisibilityStore,
- localStreamStore,
- mediaStreamConstraintsStore,
- videoConstraintStore
} from "../../Stores/MediaStore";
-import type {Unsubscriber} from "svelte/store";
export const EnableCameraSceneName = "EnableCameraScene";
-enum LoginTextures {
- playButton = "play_button",
- icon = "icon",
- mainFont = "main_font",
- arrowRight = "arrow_right",
- arrowUp = "arrow_up"
-}
-
-const enableCameraSceneKey = 'enableCameraScene';
export class EnableCameraScene extends ResizableScene {
- private textField!: TextField;
- private cameraNameField!: TextField;
- private arrowLeft!: Image;
- private arrowRight!: Image;
- private arrowDown!: Image;
- private arrowUp!: Image;
- private microphonesList: MediaDeviceInfo[] = new Array();
- private camerasList: MediaDeviceInfo[] = new Array();
- private cameraSelected: number = 0;
- private microphoneSelected: number = 0;
- private soundMeter: SoundMeter;
- private soundMeterSprite!: SoundMeterSprite;
- private microphoneNameField!: TextField;
-
- private enableCameraSceneElement!: Phaser.GameObjects.DOMElement;
-
- private mobileTapZone!: Zone;
- private localStreamStoreUnsubscriber!: Unsubscriber;
constructor() {
super({
key: EnableCameraSceneName
});
- this.soundMeter = new SoundMeter();
}
preload() {
-
- this.load.html(enableCameraSceneKey, 'resources/html/EnableCameraScene.html');
-
- this.load.image(LoginTextures.playButton, "resources/objects/play_button.png");
- this.load.image(LoginTextures.arrowRight, "resources/objects/arrow_right.png");
- this.load.image(LoginTextures.arrowUp, "resources/objects/arrow_up.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');
}
create() {
- this.enableCameraSceneElement = this.add.dom(-1000, 0).createFromCache(enableCameraSceneKey);
- this.centerXDomElement(this.enableCameraSceneElement, 300);
-
- MenuScene.revealMenusAfterInit(this.enableCameraSceneElement, enableCameraSceneKey);
-
- const continuingButton = this.enableCameraSceneElement.getChildByID('enableCameraSceneFormSubmit') as HTMLButtonElement;
- continuingButton.addEventListener('click', (e) => {
- e.preventDefault();
- this.login();
- });
-
- if (touchScreenManager.supportTouchScreen) {
- new PinchManager(this);
- }
- //this.scale.setZoom(ZOOM_LEVEL);
- //Phaser.Display.Align.In.BottomCenter(this.pressReturnField, zone);
-
- /* FIX ME */
- this.textField = new TextField(this, this.scale.width / 2, 20, '');
-
- // For mobile purposes - we need a big enough touchable area.
- this.mobileTapZone = this.add.zone(this.scale.width / 2,this.scale.height - 30,200,50)
- .setInteractive().on("pointerdown", () => {
- this.login();
- });
-
- this.cameraNameField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height - 60, '');
-
- this.microphoneNameField = new TextField(this, this.game.renderer.width / 2, this.game.renderer.height - 40, '');
-
- this.arrowRight = new Image(this, 0, 0, LoginTextures.arrowRight);
- this.arrowRight.setVisible(false);
- this.arrowRight.setInteractive().on('pointerdown', this.nextCam.bind(this));
- this.add.existing(this.arrowRight);
-
- this.arrowLeft = new Image(this, 0, 0, LoginTextures.arrowRight);
- this.arrowLeft.setVisible(false);
- this.arrowLeft.flipX = true;
- this.arrowLeft.setInteractive().on('pointerdown', this.previousCam.bind(this));
- this.add.existing(this.arrowLeft);
-
- this.arrowUp = new Image(this, 0, 0, LoginTextures.arrowUp);
- this.arrowUp.setVisible(false);
- this.arrowUp.setInteractive().on('pointerdown', this.previousMic.bind(this));
- this.add.existing(this.arrowUp);
-
- this.arrowDown = new Image(this, 0, 0, LoginTextures.arrowUp);
- this.arrowDown.setVisible(false);
- this.arrowDown.flipY = true;
- this.arrowDown.setInteractive().on('pointerdown', this.nextMic.bind(this));
- this.add.existing(this.arrowDown);
-
this.input.keyboard.on('keyup-ENTER', () => {
this.login();
});
- HtmlUtils.getElementByIdOrFail('webRtcSetup').classList.add('active');
-
- this.localStreamStoreUnsubscriber = localStreamStore.subscribe((result) => {
- if (result.type === 'error') {
- // TODO: proper handling of the error
- throw result.error;
- }
-
- this.getDevices();
- if (result.stream !== null) {
- this.setupStream(result.stream);
- }
- });
- /*const mediaPromise = mediaManager.getCamera();
- mediaPromise.then(this.getDevices.bind(this));
- mediaPromise.then(this.setupStream.bind(this));*/
-
- this.input.keyboard.on('keydown-RIGHT', this.nextCam.bind(this));
- this.input.keyboard.on('keydown-LEFT', this.previousCam.bind(this));
- this.input.keyboard.on('keydown-DOWN', this.nextMic.bind(this));
- this.input.keyboard.on('keydown-UP', this.previousMic.bind(this));
-
- this.soundMeterSprite = new SoundMeterSprite(this, 50, 50);
- this.soundMeterSprite.setVisible(false);
- this.add.existing(this.soundMeterSprite);
-
- this.onResize();
-
enableCameraSceneVisibilityStore.showEnableCameraScene();
}
- private previousCam(): void {
- if (this.cameraSelected === 0 || this.camerasList.length === 0) {
- return;
- }
- this.cameraSelected--;
- videoConstraintStore.setDeviceId(this.camerasList[this.cameraSelected].deviceId);
-
- //mediaManager.setCamera(this.camerasList[this.cameraSelected].deviceId).then(this.setupStream.bind(this));
- }
-
- private nextCam(): void {
- if (this.cameraSelected === this.camerasList.length - 1 || this.camerasList.length === 0) {
- return;
- }
- this.cameraSelected++;
- videoConstraintStore.setDeviceId(this.camerasList[this.cameraSelected].deviceId);
-
- // TODO: the change of camera should be OBSERVED (reactive)
- //mediaManager.setCamera(this.camerasList[this.cameraSelected].deviceId).then(this.setupStream.bind(this));
- }
-
- private previousMic(): void {
- if (this.microphoneSelected === 0 || this.microphonesList.length === 0) {
- return;
- }
- this.microphoneSelected--;
- audioConstraintStore.setDeviceId(this.microphonesList[this.microphoneSelected].deviceId);
- //mediaManager.setMicrophone(this.microphonesList[this.microphoneSelected].deviceId).then(this.setupStream.bind(this));
- }
-
- private nextMic(): void {
- if (this.microphoneSelected === this.microphonesList.length - 1 || this.microphonesList.length === 0) {
- return;
- }
- this.microphoneSelected++;
- audioConstraintStore.setDeviceId(this.microphonesList[this.microphoneSelected].deviceId);
- // TODO: the change of camera should be OBSERVED (reactive)
- //mediaManager.setMicrophone(this.microphonesList[this.microphoneSelected].deviceId).then(this.setupStream.bind(this));
- }
-
- /**
- * Function called each time a camera is changed
- */
- private setupStream(stream: MediaStream): void {
- const img = HtmlUtils.getElementByIdOrFail('webRtcSetupNoVideo');
- img.style.display = 'none';
-
- const div = HtmlUtils.getElementByIdOrFail('myCamVideoSetup');
- div.srcObject = stream;
-
- this.soundMeter.connectToSource(stream, new window.AudioContext());
- this.soundMeterSprite.setVisible(true);
-
- this.updateWebCamName();
- }
-
- private updateWebCamName(): void {
- if (this.camerasList.length > 1) {
- let label = this.camerasList[this.cameraSelected].label;
- // remove text in parenthesis
- label = label.replace(/\([^()]*\)/g, '').trim();
- // remove accents
- label = label.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
- this.cameraNameField.text = label;
-
- this.arrowRight.setVisible(this.cameraSelected < this.camerasList.length - 1);
- this.arrowLeft.setVisible(this.cameraSelected > 0);
- }
- if (this.microphonesList.length > 1) {
- let label = this.microphonesList[this.microphoneSelected].label;
- // remove text in parenthesis
- label = label.replace(/\([^()]*\)/g, '').trim();
- // remove accents
- label = label.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
-
- this.microphoneNameField.text = label;
-
- this.arrowDown.setVisible(this.microphoneSelected < this.microphonesList.length - 1);
- this.arrowUp.setVisible(this.microphoneSelected > 0);
-
- }
- }
-
public onResize(): void {
- let div = HtmlUtils.getElementByIdOrFail('myCamVideoSetup');
- let bounds = div.getBoundingClientRect();
- if (!div.srcObject) {
- div = HtmlUtils.getElementByIdOrFail('webRtcSetup');
- bounds = div.getBoundingClientRect();
- }
-
- this.textField.x = this.game.renderer.width / 2;
- this.mobileTapZone.x = this.game.renderer.width / 2;
- this.cameraNameField.x = this.game.renderer.width / 2;
- this.microphoneNameField.x = this.game.renderer.width / 2;
-
- this.cameraNameField.y = bounds.top / this.scale.zoom - 8;
-
- this.soundMeterSprite.x = this.game.renderer.width / 2 - this.soundMeterSprite.getWidth() / 2;
- this.soundMeterSprite.y = bounds.bottom / this.scale.zoom + 16;
-
- this.microphoneNameField.y = this.soundMeterSprite.y + 22;
-
- this.arrowRight.x = bounds.right / this.scale.zoom + 16;
- this.arrowRight.y = (bounds.top + bounds.height / 2) / this.scale.zoom;
-
- this.arrowLeft.x = bounds.left / this.scale.zoom - 16;
- this.arrowLeft.y = (bounds.top + bounds.height / 2) / this.scale.zoom;
-
- this.arrowDown.x = this.microphoneNameField.x + this.microphoneNameField.width / 2 + 16;
- this.arrowDown.y = this.microphoneNameField.y;
-
- this.arrowUp.x = this.microphoneNameField.x - this.microphoneNameField.width / 2 - 16;
- this.arrowUp.y = this.microphoneNameField.y;
-
- const actionBtn = document.querySelector('#enableCameraScene .action');
- if (actionBtn !== null) {
- actionBtn.style.top = (this.scale.height - 65) + 'px';
- }
}
update(time: number, delta: number): void {
- this.soundMeterSprite.setVolume(this.soundMeter.getVolume());
-
- this.centerXDomElement(this.enableCameraSceneElement, 300);
}
- private login(): void {
- HtmlUtils.getElementByIdOrFail('webRtcSetup').style.display = 'none';
- this.soundMeter.stop();
-
+ public login(): void {
enableCameraSceneVisibilityStore.hideEnableCameraScene();
- this.localStreamStoreUnsubscriber();
- //mediaManager.stopCamera();
- //mediaManager.stopMicrophone();
this.scene.sleep(EnableCameraSceneName);
gameManager.goToStartingMap(this.scene);
}
-
- private async getDevices() {
- // TODO: switch this in a store.
- const mediaDeviceInfos = await navigator.mediaDevices.enumerateDevices();
- this.microphonesList = [];
- this.camerasList = [];
- for (const mediaDeviceInfo of mediaDeviceInfos) {
- if (mediaDeviceInfo.kind === 'audioinput') {
- this.microphonesList.push(mediaDeviceInfo);
- } else if (mediaDeviceInfo.kind === 'videoinput') {
- this.camerasList.push(mediaDeviceInfo);
- }
- }
- this.updateWebCamName();
- }
}
diff --git a/front/src/Phaser/Login/LoginScene.ts b/front/src/Phaser/Login/LoginScene.ts
index 1219ebb7..39a8f5f3 100644
--- a/front/src/Phaser/Login/LoginScene.ts
+++ b/front/src/Phaser/Login/LoginScene.ts
@@ -1,18 +1,12 @@
import {gameManager} from "../Game/GameManager";
import {SelectCharacterSceneName} from "./SelectCharacterScene";
import {ResizableScene} from "./ResizableScene";
-import { localUserStore } from "../../Connexion/LocalUserStore";
-import {MenuScene} from "../Menu/MenuScene";
-import { isUserNameValid } from "../../Connexion/LocalUser";
-import { MAX_USERNAME_LENGTH } from "../../Enum/EnvironmentVariable";
+import {loginSceneVisibleStore} from "../../Stores/LoginSceneStore";
export const LoginSceneName = "LoginScene";
-const loginSceneKey = 'loginScene';
-
export class LoginScene extends ResizableScene {
- private loginSceneElement!: Phaser.GameObjects.DOMElement;
private name: string = '';
constructor() {
@@ -23,65 +17,25 @@ export class LoginScene extends ResizableScene {
}
preload() {
- this.load.html(loginSceneKey, 'resources/html/loginScene.html');
}
create() {
- this.loginSceneElement = this.add.dom(-1000, 0).createFromCache(loginSceneKey);
- this.centerXDomElement(this.loginSceneElement, 200);
- MenuScene.revealMenusAfterInit(this.loginSceneElement, loginSceneKey);
-
- 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 >= MAX_USERNAME_LENGTH){
- event.preventDefault();
- return;
- }
- pErrorElement.innerHTML = '';
- if(inputElement.value && !isUserNameValid(inputElement.value)){
- pErrorElement.innerHTML = 'Invalid user name: No spaces are allowed.';
- }
- 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);
- });
+ loginSceneVisibleStore.set(true);
}
- 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
- }
- if(!isUserNameValid(this.name)){
- pErrorElement.innerHTML = 'Invalid user name: only letters and numbers are allowed. No spaces.';
- return
- }
- if (this.name === '') return
- gameManager.setPlayerName(this.name);
+ public login(name: string): void {
+ name = name.trim();
+ gameManager.setPlayerName(name);
this.scene.stop(LoginSceneName)
gameManager.tryResumingGame(this, SelectCharacterSceneName);
- this.scene.remove(LoginSceneName)
+ this.scene.remove(LoginSceneName);
+ loginSceneVisibleStore.set(false);
}
update(time: number, delta: number): void {
-
}
- public onResize(ev: UIEvent): void {
- this.centerXDomElement(this.loginSceneElement, 200);
+ public onResize(): void {
}
}
diff --git a/front/src/Phaser/Login/ResizableScene.ts b/front/src/Phaser/Login/ResizableScene.ts
index 39e2d74b..d06cb66c 100644
--- a/front/src/Phaser/Login/ResizableScene.ts
+++ b/front/src/Phaser/Login/ResizableScene.ts
@@ -2,7 +2,7 @@ import {Scene} from "phaser";
import DOMElement = Phaser.GameObjects.DOMElement;
export abstract class ResizableScene extends Scene {
- public abstract onResize(ev: UIEvent): void;
+ public abstract onResize(): void;
/**
* Centers the DOM element on the X axis.
@@ -17,7 +17,7 @@ export abstract class ResizableScene extends Scene {
&& object.node
&& object.node.getBoundingClientRect().width > 0
? (object.node.getBoundingClientRect().width / 2 / this.scale.zoom)
- : (300 / this.scale.zoom)
+ : (defaultWidth / this.scale.zoom)
);
}
}
diff --git a/front/src/Phaser/Login/SelectCharacterMobileScene.ts b/front/src/Phaser/Login/SelectCharacterMobileScene.ts
index b9c4b5a8..0d8e49d5 100644
--- a/front/src/Phaser/Login/SelectCharacterMobileScene.ts
+++ b/front/src/Phaser/Login/SelectCharacterMobileScene.ts
@@ -4,49 +4,50 @@ export class SelectCharacterMobileScene extends SelectCharacterScene {
create(){
super.create();
+ this.onResize();
this.selectedRectangle.destroy();
}
- protected defineSetupPlayer(numero: number){
+ protected defineSetupPlayer(num: number){
const deltaX = 30;
const deltaY = 2;
let [playerX, playerY] = this.getCharacterPosition();
let playerVisible = true;
let playerScale = 1.5;
- let playserOpactity = 1;
+ let playerOpacity = 1;
- if( this.currentSelectUser !== numero ){
+ if( this.currentSelectUser !== num ){
playerVisible = false;
}
- if( numero === (this.currentSelectUser + 1) ){
+ if( num === (this.currentSelectUser + 1) ){
playerY -= deltaY;
playerX += deltaX;
playerScale = 0.8;
- playserOpactity = 0.6;
+ playerOpacity = 0.6;
playerVisible = true;
}
- if( numero === (this.currentSelectUser + 2) ){
+ if( num === (this.currentSelectUser + 2) ){
playerY -= deltaY;
playerX += (deltaX * 2);
playerScale = 0.8;
- playserOpactity = 0.6;
+ playerOpacity = 0.6;
playerVisible = true;
}
- if( numero === (this.currentSelectUser - 1) ){
+ if( num === (this.currentSelectUser - 1) ){
playerY -= deltaY;
playerX -= deltaX;
playerScale = 0.8;
- playserOpactity = 0.6;
+ playerOpacity = 0.6;
playerVisible = true;
}
- if( numero === (this.currentSelectUser - 2) ){
+ if( num === (this.currentSelectUser - 2) ){
playerY -= deltaY;
playerX -= (deltaX * 2);
playerScale = 0.8;
- playserOpactity = 0.6;
+ playerOpacity = 0.6;
playerVisible = true;
}
- return {playerX, playerY, playerScale, playserOpactity, playerVisible}
+ return {playerX, playerY, playerScale, playerOpacity, playerVisible}
}
/**
diff --git a/front/src/Phaser/Login/SelectCharacterScene.ts b/front/src/Phaser/Login/SelectCharacterScene.ts
index ecbb9c64..0cfbbdef 100644
--- a/front/src/Phaser/Login/SelectCharacterScene.ts
+++ b/front/src/Phaser/Login/SelectCharacterScene.ts
@@ -11,6 +11,10 @@ import {areCharacterLayersValid} from "../../Connexion/LocalUser";
import {touchScreenManager} from "../../Touch/TouchScreenManager";
import {PinchManager} from "../UserInput/PinchManager";
import {MenuScene} from "../Menu/MenuScene";
+import {selectCharacterSceneVisibleStore} from "../../Stores/SelectCharacterStore";
+import {customCharacterSceneVisibleStore} from "../../Stores/CustomCharacterStore";
+import {waScaleManager} from "../Services/WaScaleManager";
+import {isMobile} from "../../Enum/EnvironmentVariable";
//todo: put this constants in a dedicated file
export const SelectCharacterSceneName = "SelectCharacterScene";
@@ -27,6 +31,9 @@ export class SelectCharacterScene extends AbstractCharacterScene {
protected selectCharacterSceneElement!: Phaser.GameObjects.DOMElement;
protected currentSelectUser = 0;
+ protected pointerClicked: boolean = false;
+
+ protected lazyloadingAttempt = true; //permit to update texture loaded after renderer
constructor() {
super({
@@ -41,44 +48,36 @@ export class SelectCharacterScene extends AbstractCharacterScene {
bodyResourceDescriptions.forEach((bodyResourceDescription) => {
this.playerModels.push(bodyResourceDescription);
});
- })
+ this.lazyloadingAttempt = true;
+ });
this.playerModels = loadAllDefaultModels(this.load);
+ this.lazyloadingAttempt = false;
//this function must stay at the end of preload function
addLoader(this);
}
create() {
-
- this.selectCharacterSceneElement = this.add.dom(-1000, 0).createFromCache(selectCharacterKey);
- this.centerXDomElement(this.selectCharacterSceneElement, 150);
- MenuScene.revealMenusAfterInit(this.selectCharacterSceneElement, selectCharacterKey);
-
- this.selectCharacterSceneElement.addListener('click');
- this.selectCharacterSceneElement.on('click', (event:MouseEvent) => {
- event.preventDefault();
- if((event?.target as HTMLInputElement).id === 'selectCharacterButtonLeft') {
- this.moveToLeft();
- }else if((event?.target as HTMLInputElement).id === 'selectCharacterButtonRight') {
- this.moveToRight();
- }else if((event?.target as HTMLInputElement).id === 'selectCharacterSceneFormSubmit') {
- this.nextSceneToCameraScene();
- }else if((event?.target as HTMLInputElement).id === 'selectCharacterSceneFormCustomYourOwnSubmit') {
- this.nextSceneToCustomizeScene();
- }
+ selectCharacterSceneVisibleStore.set(true);
+ this.events.addListener('wake', () => {
+ waScaleManager.saveZoom();
+ waScaleManager.zoomModifier = isMobile() ? 2 : 1;
+ selectCharacterSceneVisibleStore.set(true);
});
if (touchScreenManager.supportTouchScreen) {
new PinchManager(this);
}
+ waScaleManager.saveZoom();
+ waScaleManager.zoomModifier = isMobile() ? 2 : 1;
+
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.selectedRectangle.setDepth(2);
/*create user*/
this.createCurrentPlayer();
- const playerNumber = localUserStore.getPlayerCharacterIndex();
this.input.keyboard.on('keyup-ENTER', () => {
return this.nextSceneToCameraScene();
@@ -106,9 +105,12 @@ export class SelectCharacterScene extends AbstractCharacterScene {
return;
}
this.scene.stop(SelectCharacterSceneName);
+ waScaleManager.restoreZoom();
gameManager.setCharacterLayers([this.selectedPlayer.texture.key]);
gameManager.tryResumingGame(this, EnableCameraSceneName);
- this.scene.remove(SelectCharacterSceneName);
+ this.players = [];
+ selectCharacterSceneVisibleStore.set(false);
+ this.events.removeListener('wake');
}
protected nextSceneToCustomizeScene(): void {
@@ -116,7 +118,9 @@ export class SelectCharacterScene extends AbstractCharacterScene {
return;
}
this.scene.sleep(SelectCharacterSceneName);
+ waScaleManager.restoreZoom();
this.scene.run(CustomizeSceneName);
+ selectCharacterSceneVisibleStore.set(false);
}
createCurrentPlayer(): void {
@@ -133,15 +137,16 @@ export class SelectCharacterScene extends AbstractCharacterScene {
repeat: -1
});
player.setInteractive().on("pointerdown", () => {
- if(this.currentSelectUser === i){
+ if (this.pointerClicked || this.currentSelectUser === i) {
return;
}
+ this.pointerClicked = true;
this.currentSelectUser = i;
this.moveUser();
+ setTimeout(() => {this.pointerClicked = false;}, 100);
});
this.players.push(player);
}
-
this.selectedPlayer = this.players[this.currentSelectUser];
this.selectedPlayer.play(this.playerModels[this.currentSelectUser].name);
}
@@ -186,35 +191,35 @@ export class SelectCharacterScene extends AbstractCharacterScene {
this.moveUser();
}
- protected defineSetupPlayer(numero: number){
+ protected defineSetupPlayer(num: number){
const deltaX = 32;
const deltaY = 32;
let [playerX, playerY] = this.getCharacterPosition(); // player X and player y are middle of the
- playerX = ( (playerX - (deltaX * 2.5)) + ((deltaX) * (numero % this.nbCharactersPerRow)) ); // calcul position on line users
- playerY = ( (playerY - (deltaY * 2)) + ((deltaY) * ( Math.floor(numero / this.nbCharactersPerRow) )) ); // calcul position on column users
+ playerX = ( (playerX - (deltaX * 2.5)) + ((deltaX) * (num % this.nbCharactersPerRow)) ); // calcul position on line users
+ playerY = ( (playerY - (deltaY * 2)) + ((deltaY) * ( Math.floor(num / this.nbCharactersPerRow) )) ); // calcul position on column users
const playerVisible = true;
const playerScale = 1;
- const playserOpactity = 1;
+ const playerOpacity = 1;
// if selected
- if( numero === this.currentSelectUser ){
+ if( num === this.currentSelectUser ){
this.selectedRectangle.setX(playerX);
this.selectedRectangle.setY(playerY);
}
- return {playerX, playerY, playerScale, playserOpactity, playerVisible}
+ return {playerX, playerY, playerScale, playerOpacity, playerVisible}
}
- protected setUpPlayer(player: Phaser.Physics.Arcade.Sprite, numero: number){
+ protected setUpPlayer(player: Phaser.Physics.Arcade.Sprite, num: number){
- const {playerX, playerY, playerScale, playserOpactity, playerVisible} = this.defineSetupPlayer(numero);
+ const {playerX, playerY, playerScale, playerOpacity, playerVisible} = this.defineSetupPlayer(num);
player.setBounce(0.2);
- player.setCollideWorldBounds(true);
+ player.setCollideWorldBounds(false);
player.setVisible( playerVisible );
player.setScale(playerScale, playerScale);
- player.setAlpha(playserOpactity);
+ player.setAlpha(playerOpacity);
player.setX(playerX);
player.setY(playerY);
}
@@ -238,12 +243,14 @@ export class SelectCharacterScene extends AbstractCharacterScene {
}
update(time: number, delta: number): void {
+ if(this.lazyloadingAttempt){
+ this.createCurrentPlayer();
+ this.lazyloadingAttempt = false;
+ }
}
- public onResize(ev: UIEvent): void {
+ public onResize(): void {
//move position of user
this.moveUser();
-
- this.centerXDomElement(this.selectCharacterSceneElement, 150);
}
}
diff --git a/front/src/Phaser/Login/SelectCompanionScene.ts b/front/src/Phaser/Login/SelectCompanionScene.ts
index 203fd557..9caa88f7 100644
--- a/front/src/Phaser/Login/SelectCompanionScene.ts
+++ b/front/src/Phaser/Login/SelectCompanionScene.ts
@@ -10,17 +10,18 @@ import { getAllCompanionResources } from "../Companion/CompanionTexturesLoadingM
import {touchScreenManager} from "../../Touch/TouchScreenManager";
import {PinchManager} from "../UserInput/PinchManager";
import { MenuScene } from "../Menu/MenuScene";
+import {selectCompanionSceneVisibleStore} from "../../Stores/SelectCompanionStore";
+import {waScaleManager} from "../Services/WaScaleManager";
+import {isMobile} from "../../Enum/EnvironmentVariable";
export const SelectCompanionSceneName = "SelectCompanionScene";
-const selectCompanionSceneKey = 'selectCompanionScene';
-
export class SelectCompanionScene extends ResizableScene {
private selectedCompanion!: Phaser.Physics.Arcade.Sprite;
private companions: Array = new Array();
private companionModels: Array = [];
+ private saveZoom: number = 0;
- private selectCompanionSceneElement!: Phaser.GameObjects.DOMElement;
private currentCompanion = 0;
constructor() {
@@ -30,8 +31,6 @@ export class SelectCompanionScene extends ResizableScene {
}
preload() {
- this.load.html(selectCompanionSceneKey, 'resources/html/SelectCompanionScene.html');
-
getAllCompanionResources(this.load).forEach(model => {
this.companionModels.push(model);
});
@@ -42,30 +41,17 @@ export class SelectCompanionScene extends ResizableScene {
create() {
- this.selectCompanionSceneElement = this.add.dom(-1000, 0).createFromCache(selectCompanionSceneKey);
- this.centerXDomElement(this.selectCompanionSceneElement, 150);
- MenuScene.revealMenusAfterInit(this.selectCompanionSceneElement, selectCompanionSceneKey);
+ selectCompanionSceneVisibleStore.set(true);
- this.selectCompanionSceneElement.addListener('click');
- this.selectCompanionSceneElement.on('click', (event:MouseEvent) => {
- event.preventDefault();
- if((event?.target as HTMLInputElement).id === 'selectCharacterButtonLeft') {
- this.moveToLeft();
- }else if((event?.target as HTMLInputElement).id === 'selectCharacterButtonRight') {
- this.moveToRight();
- }else if((event?.target as HTMLInputElement).id === 'selectCompanionSceneFormSubmit') {
- this.nextScene();
- }else if((event?.target as HTMLInputElement).id === 'selectCompanionSceneFormBack') {
- this._nextScene();
- }
- });
+ waScaleManager.saveZoom();
+ waScaleManager.zoomModifier = isMobile() ? 2 : 1;
if (touchScreenManager.supportTouchScreen) {
new PinchManager(this);
}
// input events
- this.input.keyboard.on('keyup-ENTER', this.nextScene.bind(this));
+ this.input.keyboard.on('keyup-ENTER', this.selectCompanion.bind(this));
this.input.keyboard.on('keydown-RIGHT', this.moveToRight.bind(this));
this.input.keyboard.on('keydown-LEFT', this.moveToLeft.bind(this));
@@ -89,18 +75,20 @@ export class SelectCompanionScene extends ResizableScene {
}
- private nextScene(): void {
+ public selectCompanion(): void {
localUserStore.setCompanion(this.companionModels[this.currentCompanion].name);
gameManager.setCompanion(this.companionModels[this.currentCompanion].name);
- this._nextScene();
+ this.closeScene();
}
- private _nextScene(){
+ public closeScene(){
// next scene
this.scene.stop(SelectCompanionSceneName);
+ waScaleManager.restoreZoom();
gameManager.tryResumingGame(this, EnableCameraSceneName);
this.scene.remove(SelectCompanionSceneName);
+ selectCompanionSceneVisibleStore.set(false);
}
private createCurrentCompanion(): void {
@@ -126,10 +114,8 @@ export class SelectCompanionScene extends ResizableScene {
this.selectedCompanion = this.companions[this.currentCompanion];
}
- public onResize(ev: UIEvent): void {
+ public onResize(): void {
this.moveCompanion();
-
- this.centerXDomElement(this.selectCompanionSceneElement, 150);
}
private updateSelectedCompanion(): void {
@@ -147,15 +133,7 @@ export class SelectCompanionScene extends ResizableScene {
this.updateSelectedCompanion();
}
- private moveToLeft(){
- if(this.currentCompanion === 0){
- return;
- }
- this.currentCompanion -= 1;
- this.moveCompanion();
- }
-
- private moveToRight(){
+ public moveToRight(){
if(this.currentCompanion === (this.companions.length - 1)){
return;
}
@@ -163,38 +141,46 @@ export class SelectCompanionScene extends ResizableScene {
this.moveCompanion();
}
- private defineSetupCompanion(numero: number){
+ public moveToLeft(){
+ if(this.currentCompanion === 0){
+ return;
+ }
+ this.currentCompanion -= 1;
+ this.moveCompanion();
+ }
+
+ private defineSetupCompanion(num: number){
const deltaX = 30;
const deltaY = 2;
let [companionX, companionY] = this.getCompanionPosition();
let companionVisible = true;
let companionScale = 1.5;
let companionOpactity = 1;
- if( this.currentCompanion !== numero ){
+ if( this.currentCompanion !== num ){
companionVisible = false;
}
- if( numero === (this.currentCompanion + 1) ){
+ if( num === (this.currentCompanion + 1) ){
companionY -= deltaY;
companionX += deltaX;
companionScale = 0.8;
companionOpactity = 0.6;
companionVisible = true;
}
- if( numero === (this.currentCompanion + 2) ){
+ if( num === (this.currentCompanion + 2) ){
companionY -= deltaY;
companionX += (deltaX * 2);
companionScale = 0.8;
companionOpactity = 0.6;
companionVisible = true;
}
- if( numero === (this.currentCompanion - 1) ){
+ if( num === (this.currentCompanion - 1) ){
companionY -= deltaY;
companionX -= deltaX;
companionScale = 0.8;
companionOpactity = 0.6;
companionVisible = true;
}
- if( numero === (this.currentCompanion - 2) ){
+ if( num === (this.currentCompanion - 2) ){
companionY -= deltaY;
companionX -= (deltaX * 2);
companionScale = 0.8;
diff --git a/front/src/Phaser/Menu/HelpCameraSettingsScene.ts b/front/src/Phaser/Menu/HelpCameraSettingsScene.ts
deleted file mode 100644
index 6bc520c0..00000000
--- a/front/src/Phaser/Menu/HelpCameraSettingsScene.ts
+++ /dev/null
@@ -1,152 +0,0 @@
-import {mediaManager} from "../../WebRtc/MediaManager";
-import {HtmlUtils} from "../../WebRtc/HtmlUtils";
-import {localUserStore} from "../../Connexion/LocalUserStore";
-import {DirtyScene} from "../Game/DirtyScene";
-import {get} from "svelte/store";
-import {requestedCameraState, requestedMicrophoneState} from "../../Stores/MediaStore";
-
-export const HelpCameraSettingsSceneName = 'HelpCameraSettingsScene';
-const helpCameraSettings = 'helpCameraSettings';
-/**
- * The scene that show how to permit Camera and Microphone access if there are not already allowed
- */
-export class HelpCameraSettingsScene extends DirtyScene {
- private helpCameraSettingsElement!: Phaser.GameObjects.DOMElement;
- private helpCameraSettingsOpened: boolean = false;
-
- constructor() {
- super({key: HelpCameraSettingsSceneName});
- }
-
- preload() {
- this.load.html(helpCameraSettings, 'resources/html/helpCameraSettings.html');
- }
-
- create(){
- this.createHelpCameraSettings();
- }
-
- private createHelpCameraSettings() : void {
- const middleX = this.getMiddleX();
- this.helpCameraSettingsElement = this.add.dom(middleX, -800, undefined, {overflow: 'scroll'}).createFromCache(helpCameraSettings);
- this.revealMenusAfterInit(this.helpCameraSettingsElement, helpCameraSettings);
- this.helpCameraSettingsElement.addListener('click');
- this.helpCameraSettingsElement.on('click', (event:MouseEvent) => {
- if((event?.target as HTMLInputElement).id === 'mailto') {
- return;
- }
- event.preventDefault();
- if((event?.target as HTMLInputElement).id === 'helpCameraSettingsFormRefresh') {
- window.location.reload();
- }else if((event?.target as HTMLInputElement).id === 'helpCameraSettingsFormContinue') {
- this.closeHelpCameraSettingsOpened();
- }
- });
-
- if(!localUserStore.getHelpCameraSettingsShown() && (!get(requestedMicrophoneState) || !get(requestedCameraState))){
- this.openHelpCameraSettingsOpened();
- localUserStore.setHelpCameraSettingsShown();
- }
-
- mediaManager.setHelpCameraSettingsCallBack(() => {
- this.openHelpCameraSettingsOpened();
- });
- }
-
- private openHelpCameraSettingsOpened(): void{
- HtmlUtils.getElementByIdOrFail('webRtcSetup').style.display = 'none';
- this.helpCameraSettingsOpened = true;
- try{
- if(window.navigator.userAgent.includes('Firefox')){
- HtmlUtils.getElementByIdOrFail('browserHelpSetting').innerHTML =' ';
- }else if(window.navigator.userAgent.includes('Chrome')){
- HtmlUtils.getElementByIdOrFail('browserHelpSetting').innerHTML =' ';
- }
- }catch(err) {
- console.error('openHelpCameraSettingsOpened => getElementByIdOrFail => error', err);
- }
- const middleY = this.getMiddleY();
- const middleX = this.getMiddleX();
- this.tweens.add({
- targets: this.helpCameraSettingsElement,
- y: middleY,
- x: middleX,
- duration: 1000,
- ease: 'Power3',
- overflow: 'scroll'
- });
-
- this.dirty = true;
- }
-
- private closeHelpCameraSettingsOpened(): void{
- const middleX = this.getMiddleX();
- /*const helpCameraSettingsInfo = this.helpCameraSettingsElement.getChildByID('helpCameraSettings') as HTMLParagraphElement;
- helpCameraSettingsInfo.innerText = '';
- helpCameraSettingsInfo.style.display = 'none';*/
- this.helpCameraSettingsOpened = false;
- this.tweens.add({
- targets: this.helpCameraSettingsElement,
- y: -1000,
- x: middleX,
- duration: 1000,
- ease: 'Power3',
- overflow: 'scroll'
- });
-
- this.dirty = true;
- }
-
- private revealMenusAfterInit(menuElement: Phaser.GameObjects.DOMElement, rootDomId: string) {
- //Dom elements will appear inside the viewer screen when creating before being moved out of it, which create a flicker effect.
- //To prevent this, we put a 'hidden' attribute on the root element, we remove it only after the init is done.
- setTimeout(() => {
- (menuElement.getChildByID(rootDomId) as HTMLElement).hidden = false;
- }, 250);
- }
-
- update(time: number, delta: number): void {
- this.dirty = false;
- }
-
- public onResize(ev: UIEvent): void {
- super.onResize(ev);
- if (this.helpCameraSettingsOpened) {
- const middleX = this.getMiddleX();
- const middleY = this.getMiddleY();
- this.tweens.add({
- targets: this.helpCameraSettingsElement,
- x: middleX,
- y: middleY,
- duration: 1000,
- ease: 'Power3'
- });
- this.dirty = true;
- }
- }
-
- private getMiddleX() : number{
- return (this.scale.width / 2) -
- (
- this.helpCameraSettingsElement
- && this.helpCameraSettingsElement.node
- && this.helpCameraSettingsElement.node.getBoundingClientRect().width > 0
- ? (this.helpCameraSettingsElement.node.getBoundingClientRect().width / (2 * this.scale.zoom))
- : (400 / 2)
- );
- }
-
- private getMiddleY() : number{
- const middleY = ((this.scale.height) - (
- (this.helpCameraSettingsElement
- && this.helpCameraSettingsElement.node
- && this.helpCameraSettingsElement.node.getBoundingClientRect().height > 0
- ? this.helpCameraSettingsElement.node.getBoundingClientRect().height : 400 /*FIXME to use a const will be injected in HTMLElement*/)/this.scale.zoom)) / 2;
- return (middleY > 0 ? middleY : 0);
- }
-
- public isDirty(): boolean {
- return this.dirty;
- }
-}
-
diff --git a/front/src/Phaser/Services/WaScaleManager.ts b/front/src/Phaser/Services/WaScaleManager.ts
index ca8b668d..4e0e9208 100644
--- a/front/src/Phaser/Services/WaScaleManager.ts
+++ b/front/src/Phaser/Services/WaScaleManager.ts
@@ -2,6 +2,7 @@ import {HdpiManager} from "./HdpiManager";
import ScaleManager = Phaser.Scale.ScaleManager;
import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager";
import type {Game} from "../Game/Game";
+import {ResizableScene} from "../Login/ResizableScene";
class WaScaleManager {
@@ -9,6 +10,7 @@ class WaScaleManager {
private scaleManager!: ScaleManager;
private game!: Game;
private actualZoom: number = 1;
+ private _saveZoom: number = 1;
public constructor(private minGamePixelsNumber: number, private absoluteMinPixelNumber: number) {
this.hdpiManager = new HdpiManager(minGamePixelsNumber, absoluteMinPixelNumber);
@@ -30,13 +32,19 @@ class WaScaleManager {
const { game: gameSize, real: realSize } = this.hdpiManager.getOptimalGameSize({width: width * devicePixelRatio, height: height * devicePixelRatio});
this.actualZoom = realSize.width / gameSize.width / devicePixelRatio;
- this.scaleManager.setZoom(realSize.width / gameSize.width / devicePixelRatio);
+ this.scaleManager.setZoom(realSize.width / gameSize.width / devicePixelRatio)
this.scaleManager.resize(gameSize.width, gameSize.height);
// Override bug in canvas resizing in Phaser. Let's resize the canvas ourselves
const style = this.scaleManager.canvas.style;
style.width = Math.ceil(realSize.width / devicePixelRatio) + 'px';
style.height = Math.ceil(realSize.height / devicePixelRatio) + 'px';
+ // Note: onResize will be called twice (once here and once is Game.ts), but we have no better way.
+ for (const scene of this.game.scene.getScenes(true)) {
+ if (scene instanceof ResizableScene) {
+ scene.onResize();
+ }
+ }
this.game.markDirty();
}
@@ -50,6 +58,16 @@ class WaScaleManager {
this.applyNewSize();
}
+ public saveZoom(): void {
+ this._saveZoom = this.hdpiManager.zoomModifier;
+ console.log(this._saveZoom);
+ }
+
+ public restoreZoom(): void{
+ this.hdpiManager.zoomModifier = this._saveZoom;
+ this.applyNewSize();
+ }
+
/**
* This is used to scale back the ui components to counter-act the zoom.
*/
diff --git a/front/src/Stores/CustomCharacterStore.ts b/front/src/Stores/CustomCharacterStore.ts
new file mode 100644
index 00000000..4bef7768
--- /dev/null
+++ b/front/src/Stores/CustomCharacterStore.ts
@@ -0,0 +1,3 @@
+import { derived, writable, Writable } from "svelte/store";
+
+export const customCharacterSceneVisibleStore = writable(false);
\ No newline at end of file
diff --git a/front/src/Stores/HelpCameraSettingsStore.ts b/front/src/Stores/HelpCameraSettingsStore.ts
new file mode 100644
index 00000000..88373dab
--- /dev/null
+++ b/front/src/Stores/HelpCameraSettingsStore.ts
@@ -0,0 +1,3 @@
+import { writable } from "svelte/store";
+
+export const helpCameraSettingsVisibleStore = writable(false);
diff --git a/front/src/Stores/LoginSceneStore.ts b/front/src/Stores/LoginSceneStore.ts
new file mode 100644
index 00000000..6e2ea18b
--- /dev/null
+++ b/front/src/Stores/LoginSceneStore.ts
@@ -0,0 +1,3 @@
+import { writable } from "svelte/store";
+
+export const loginSceneVisibleStore = writable(false);
diff --git a/front/src/Stores/MediaStore.ts b/front/src/Stores/MediaStore.ts
index 9e53aa3b..7d1911a4 100644
--- a/front/src/Stores/MediaStore.ts
+++ b/front/src/Stores/MediaStore.ts
@@ -209,10 +209,14 @@ function createVideoConstraintStore() {
return {
subscribe,
- setDeviceId: (deviceId: string) => update((constraints) => {
- constraints.deviceId = {
- exact: deviceId
- };
+ setDeviceId: (deviceId: string|undefined) => update((constraints) => {
+ if (deviceId !== undefined) {
+ constraints.deviceId = {
+ exact: deviceId
+ };
+ } else {
+ delete constraints.deviceId;
+ }
return constraints;
}),
@@ -241,15 +245,19 @@ function createAudioConstraintStore() {
return {
subscribe,
- setDeviceId: (deviceId: string) => update((constraints) => {
+ setDeviceId: (deviceId: string|undefined) => update((constraints) => {
selectedDeviceId = deviceId;
if (typeof(constraints) === 'boolean') {
constraints = {}
}
- constraints.deviceId = {
- exact: selectedDeviceId
- };
+ if (deviceId !== undefined) {
+ constraints.deviceId = {
+ exact: selectedDeviceId
+ };
+ } else {
+ delete constraints.deviceId;
+ }
return constraints;
})
@@ -410,13 +418,15 @@ export const localStreamStore = derived, LocalS
error: new Error('Unable to access your camera or microphone. You need to use a HTTPS connection.'),
constraints
});
+ return;
} else {
//throw new Error('Unable to access your camera or microphone. Your browser is too old.');
set({
type: 'error',
- error: new Error('Unable to access your camera or microphone. Your browser is too old.'),
+ error: new Error('Unable to access your camera or microphone. Your browser is too old. Please consider upgrading your browser or try using a recent version of Chrome.'),
constraints
});
+ return;
}
}
@@ -508,3 +518,79 @@ export const obtainedMediaConstraintStore = derived(localStreamStore, ($localStr
return $localStreamStore.constraints;
});
+/**
+ * Device list
+ */
+export const deviceListStore = readable([], function start(set) {
+ let deviceListCanBeQueried = false;
+
+ const queryDeviceList = () => {
+ // Note: so far, we are ignoring any failures.
+ navigator.mediaDevices.enumerateDevices().then((mediaDeviceInfos) => {
+ set(mediaDeviceInfos);
+ }).catch((e) => {
+ console.error(e);
+ throw e;
+ });
+ };
+
+ const unsubscribe = localStreamStore.subscribe((streamResult) => {
+ if (streamResult.type === "success" && streamResult.stream !== null) {
+ if (deviceListCanBeQueried === false) {
+ queryDeviceList();
+ deviceListCanBeQueried = true;
+ }
+ }
+ });
+
+ if (navigator.mediaDevices) {
+ navigator.mediaDevices.addEventListener('devicechange', queryDeviceList);
+ }
+
+ return function stop() {
+ unsubscribe();
+ if (navigator.mediaDevices) {
+ navigator.mediaDevices.removeEventListener('devicechange', queryDeviceList);
+ }
+ };
+});
+
+export const cameraListStore = derived(deviceListStore, ($deviceListStore) => {
+ return $deviceListStore.filter(device => device.kind === 'videoinput');
+});
+
+export const microphoneListStore = derived(deviceListStore, ($deviceListStore) => {
+ return $deviceListStore.filter(device => device.kind === 'audioinput');
+});
+
+// TODO: detect the new webcam and automatically switch on it.
+cameraListStore.subscribe((devices) => {
+ // If the selected camera is unplugged, let's remove the constraint on deviceId
+ const constraints = get(videoConstraintStore);
+ if (!constraints.deviceId) {
+ return;
+ }
+
+ // If we cannot find the device ID, let's remove it.
+ // @ts-ignore
+ if (!devices.find(device => device.deviceId === constraints.deviceId.exact)) {
+ videoConstraintStore.setDeviceId(undefined);
+ }
+});
+
+microphoneListStore.subscribe((devices) => {
+ // If the selected camera is unplugged, let's remove the constraint on deviceId
+ const constraints = get(audioConstraintStore);
+ if (typeof constraints === 'boolean') {
+ return;
+ }
+ if (!constraints.deviceId) {
+ return;
+ }
+
+ // If we cannot find the device ID, let's remove it.
+ // @ts-ignore
+ if (!devices.find(device => device.deviceId === constraints.deviceId.exact)) {
+ audioConstraintStore.setDeviceId(undefined);
+ }
+});
diff --git a/front/src/Stores/ScreenSharingStore.ts b/front/src/Stores/ScreenSharingStore.ts
index a55f5ab2..0a7ef3e6 100644
--- a/front/src/Stores/ScreenSharingStore.ts
+++ b/front/src/Stores/ScreenSharingStore.ts
@@ -126,7 +126,7 @@ export const screenSharingLocalStreamStore = derived;
if (navigator.getDisplayMedia) {
currentStreamPromise = navigator.getDisplayMedia({constraints});
- } else if (navigator.mediaDevices.getDisplayMedia) {
+ } else if (navigator.mediaDevices && navigator.mediaDevices.getDisplayMedia) {
currentStreamPromise = navigator.mediaDevices.getDisplayMedia({constraints});
} else {
stopScreenSharing();
@@ -183,7 +183,7 @@ export const screenSharingLocalStreamStore = derived {
- if (!navigator.getDisplayMedia && !navigator.mediaDevices.getDisplayMedia) {
+ if (!navigator.getDisplayMedia && (!navigator.mediaDevices || !navigator.mediaDevices.getDisplayMedia)) {
set(false);
return;
}
diff --git a/front/src/Stores/SelectCharacterStore.ts b/front/src/Stores/SelectCharacterStore.ts
new file mode 100644
index 00000000..094eaef3
--- /dev/null
+++ b/front/src/Stores/SelectCharacterStore.ts
@@ -0,0 +1,3 @@
+import { derived, writable, Writable } from "svelte/store";
+
+export const selectCharacterSceneVisibleStore = writable(false);
\ No newline at end of file
diff --git a/front/src/Stores/SelectCompanionStore.ts b/front/src/Stores/SelectCompanionStore.ts
new file mode 100644
index 00000000..e66f5de3
--- /dev/null
+++ b/front/src/Stores/SelectCompanionStore.ts
@@ -0,0 +1,3 @@
+import { derived, writable, Writable } from "svelte/store";
+
+export const selectCompanionSceneVisibleStore = writable(false);
diff --git a/front/src/Stores/SoundPlayingStore.ts b/front/src/Stores/SoundPlayingStore.ts
new file mode 100644
index 00000000..cf1d681c
--- /dev/null
+++ b/front/src/Stores/SoundPlayingStore.ts
@@ -0,0 +1,22 @@
+import { writable } from "svelte/store";
+
+/**
+ * A store that contains the URL of the sound currently playing
+ */
+function createSoundPlayingStore() {
+ const { subscribe, set, update } = writable(null);
+
+ return {
+ subscribe,
+ playSound: (url: string) => {
+ set(url);
+ },
+ soundEnded: () => {
+ set(null);
+ }
+
+
+ };
+}
+
+export const soundPlayingStore = createSoundPlayingStore();
diff --git a/front/src/WebRtc/MediaManager.ts b/front/src/WebRtc/MediaManager.ts
index 03990ce8..7b527962 100644
--- a/front/src/WebRtc/MediaManager.ts
+++ b/front/src/WebRtc/MediaManager.ts
@@ -8,32 +8,11 @@ import {SoundMeter} from "../Phaser/Components/SoundMeter";
import {DISABLE_NOTIFICATIONS} from "../Enum/EnvironmentVariable";
import {
gameOverlayVisibilityStore, localStreamStore,
- mediaStreamConstraintsStore,
- requestedCameraState,
- requestedMicrophoneState
} from "../Stores/MediaStore";
import {
- requestedScreenSharingState,
- screenSharingAvailableStore,
screenSharingLocalStreamStore
} from "../Stores/ScreenSharingStore";
-
-declare const navigator:any; // eslint-disable-line @typescript-eslint/no-explicit-any
-
-const videoConstraint: boolean|MediaTrackConstraints = {
- width: { min: 640, ideal: 1280, max: 1920 },
- height: { min: 400, ideal: 720 },
- frameRate: { ideal: localUserStore.getVideoQualityValue() },
- facingMode: "user",
- resizeMode: 'crop-and-scale',
- aspectRatio: 1.777777778
-};
-const audioConstraint: MediaTrackConstraints = {
- // TODO: Make these values configurable in the game settings menu and store them in local storage
- autoGainControl: false,
- echoCancellation: true,
- noiseSuppression: true
-};
+import {helpCameraSettingsVisibleStore} from "../Stores/HelpCameraSettingsStore";
export type UpdatedLocalStreamCallback = (media: MediaStream|null) => void;
export type StartScreenSharingCallback = (media: MediaStream) => void;
@@ -43,18 +22,14 @@ export type ShowReportCallBack = (userId: string, userName: string|undefined) =>
export type HelpCameraSettingsCallBack = () => void;
export class MediaManager {
- localStream: MediaStream|null = null;
- localScreenCapture: MediaStream|null = null;
private remoteVideo: Map = new Map();
webrtcInAudio: HTMLAudioElement;
//FIX ME SOUNDMETER: check stalability of sound meter calculation
//mySoundMeterElement: HTMLDivElement;
private webrtcOutAudio: HTMLAudioElement;
- updatedLocalStreamCallBacks : Set = new Set();
startScreenSharingCallBacks : Set = new Set();
stopScreenSharingCallBacks : Set = new Set();
showReportModalCallBacks : Set = new Set();
- helpCameraSettingsCallBacks : Set = new Set();
private focused : boolean = true;
@@ -88,76 +63,27 @@ export class MediaManager {
localStreamStore.subscribe((result) => {
if (result.type === 'error') {
console.error(result.error);
- layoutManager.addInformation('warning', 'Camera access denied. Click here and check navigators permissions.', () => {
- this.showHelpCameraSettingsCallBack();
+ layoutManager.addInformation('warning', 'Camera access denied. Click here and check your browser permissions.', () => {
+ helpCameraSettingsVisibleStore.set(true);
}, this.userInputManager);
return;
}
-
- /*if (result.constraints.video !== false) {
- HtmlUtils.getElementByIdOrFail('div-myCamVideo').classList.remove('hide');
- } else {
- HtmlUtils.getElementByIdOrFail('div-myCamVideo').classList.add('hide');
- }
- if (result.constraints.audio !== false) {
- this.enableMicrophoneStyle();
- } else {
- this.disableMicrophoneStyle();
- }*/
-
- this.localStream = result.stream;
- //this.myCamVideo.srcObject = this.localStream;
-
- // TODO: migrate all listeners to the store directly.
- this.triggerUpdatedLocalStreamCallbacks(result.stream);
});
- /*requestedCameraState.subscribe((enabled) => {
- if (enabled) {
- this.enableCameraStyle();
- } else {
- this.disableCameraStyle();
- }
- });
- requestedMicrophoneState.subscribe((enabled) => {
- if (enabled) {
- this.enableMicrophoneStyle();
- } else {
- this.disableMicrophoneStyle();
- }
- });*/
- //let screenSharingStream : MediaStream|null;
screenSharingLocalStreamStore.subscribe((result) => {
if (result.type === 'error') {
console.error(result.error);
- layoutManager.addInformation('warning', 'Screen sharing denied. Click here and check navigators permissions.', () => {
- this.showHelpCameraSettingsCallBack();
+ layoutManager.addInformation('warning', 'Screen sharing denied. Click here and check your browser permissions.', () => {
+ helpCameraSettingsVisibleStore.set(true);
}, this.userInputManager);
return;
}
if (result.stream !== null) {
- //this.enableScreenSharingStyle();
- mediaManager.localScreenCapture = result.stream;
-
- // TODO: migrate this out of MediaManager
- this.triggerStartedScreenSharingCallbacks(result.stream);
-
- //screenSharingStream = result.stream;
-
this.addScreenSharingActiveVideo('me', DivImportance.Normal);
HtmlUtils.getElementByIdOrFail('screen-sharing-me').srcObject = result.stream;
} else {
- //this.disableScreenSharingStyle();
this.removeActiveScreenSharingVideo('me');
-
- // FIXME: we need the old stream that is being stopped!
- if (this.localScreenCapture) {
- this.triggerStoppedScreenSharingCallbacks(this.localScreenCapture);
- this.localScreenCapture = null;
- }
-
- //screenSharingStream = null;
}
});
@@ -176,40 +102,6 @@ export class MediaManager {
//this.updateSoudMeter();
}
- public onUpdateLocalStream(callback: UpdatedLocalStreamCallback): void {
- this.updatedLocalStreamCallBacks.add(callback);
- }
-
- public onStartScreenSharing(callback: StartScreenSharingCallback): void {
- this.startScreenSharingCallBacks.add(callback);
- }
-
- public onStopScreenSharing(callback: StopScreenSharingCallback): void {
- this.stopScreenSharingCallBacks.add(callback);
- }
-
- removeUpdateLocalStreamEventListener(callback: UpdatedLocalStreamCallback): void {
- this.updatedLocalStreamCallBacks.delete(callback);
- }
-
- private triggerUpdatedLocalStreamCallbacks(stream: MediaStream|null): void {
- for (const callback of this.updatedLocalStreamCallBacks) {
- callback(stream);
- }
- }
-
- private triggerStartedScreenSharingCallbacks(stream: MediaStream): void {
- for (const callback of this.startScreenSharingCallBacks) {
- callback(stream);
- }
- }
-
- private triggerStoppedScreenSharingCallbacks(stream: MediaStream): void {
- for (const callback of this.stopScreenSharingCallBacks) {
- callback(stream);
- }
- }
-
public showGameOverlay(): void {
const gameOverlay = HtmlUtils.getElementByIdOrFail('game-overlay');
gameOverlay.classList.add('active');
@@ -236,42 +128,6 @@ export class MediaManager {
gameOverlayVisibilityStore.hideGameOverlay();
}
- /*private enableCameraStyle(){
- this.cinemaClose.style.display = "none";
- this.cinemaBtn.classList.remove("disabled");
- this.cinema.style.display = "block";
- }
-
- private disableCameraStyle(){
- this.cinemaClose.style.display = "block";
- this.cinema.style.display = "none";
- this.cinemaBtn.classList.add("disabled");
- }
-
- private enableMicrophoneStyle(){
- this.microphoneClose.style.display = "none";
- this.microphone.style.display = "block";
- this.microphoneBtn.classList.remove("disabled");
- }
-
- private disableMicrophoneStyle(){
- this.microphoneClose.style.display = "block";
- this.microphone.style.display = "none";
- this.microphoneBtn.classList.add("disabled");
- }
-
- private enableScreenSharingStyle(){
- this.monitorClose.style.display = "none";
- this.monitor.style.display = "block";
- this.monitorBtn.classList.add("enabled");
- }
-
- private disableScreenSharingStyle(){
- this.monitorClose.style.display = "block";
- this.monitor.style.display = "none";
- this.monitorBtn.classList.remove("enabled");
- }*/
-
addActiveVideo(user: UserSimplePeerInterface, userName: string = ""){
this.webrtcInAudio.play();
const userId = ''+user.userId
@@ -539,16 +395,6 @@ export class MediaManager {
this.showReportModalCallBacks.add(callback);
}
- public setHelpCameraSettingsCallBack(callback: HelpCameraSettingsCallBack){
- this.helpCameraSettingsCallBacks.add(callback);
- }
-
- private showHelpCameraSettingsCallBack(){
- for(const callBack of this.helpCameraSettingsCallBacks){
- callBack();
- }
- }
-
//FIX ME SOUNDMETER: check stalability of sound meter calculation
/*updateSoudMeter(){
try{
@@ -594,12 +440,32 @@ export class MediaManager {
public getNotification(){
//Get notification
if (!DISABLE_NOTIFICATIONS && window.Notification && Notification.permission !== "granted") {
- Notification.requestPermission().catch((err) => {
- console.error(`Notification permission error`, err);
- });
+ if (this.checkNotificationPromise()) {
+ Notification.requestPermission().catch((err) => {
+ console.error(`Notification permission error`, err);
+ });
+ } else {
+ Notification.requestPermission();
+ }
}
}
+ /**
+ * Return true if the browser supports the modern version of the Notification API (which is Promise based) or false
+ * if we are on Safari...
+ *
+ * See https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API/Using_the_Notifications_API
+ */
+ private checkNotificationPromise(): boolean {
+ try {
+ Notification.requestPermission().then();
+ } catch(e) {
+ return false;
+ }
+
+ return true;
+ }
+
public createNotification(userName: string){
if(this.focused){
return;
diff --git a/front/src/WebRtc/ScreenSharingPeer.ts b/front/src/WebRtc/ScreenSharingPeer.ts
index f1786ef3..d797f59b 100644
--- a/front/src/WebRtc/ScreenSharingPeer.ts
+++ b/front/src/WebRtc/ScreenSharingPeer.ts
@@ -19,7 +19,7 @@ export class ScreenSharingPeer extends Peer {
public _connected: boolean = false;
private userId: number;
- constructor(user: UserSimplePeerInterface, initiator: boolean, private connection: RoomConnection) {
+ constructor(user: UserSimplePeerInterface, initiator: boolean, private connection: RoomConnection, stream: MediaStream | null) {
super({
initiator: initiator ? initiator : false,
//reconnectTimer: 10000,
@@ -60,6 +60,7 @@ export class ScreenSharingPeer extends Peer {
const message = JSON.parse(chunk.toString('utf8'));
if (message.streamEnded !== true) {
console.error('Unexpected message on screen sharing peer connection');
+ return;
}
mediaManager.removeActiveScreenSharingVideo("" + this.userId);
});
@@ -81,7 +82,9 @@ export class ScreenSharingPeer extends Peer {
this._onFinish();
});
- this.pushScreenSharingToRemoteUser();
+ if (stream) {
+ this.addStream(stream);
+ }
}
private sendWebrtcScreenSharingSignal(data: unknown) {
@@ -141,16 +144,6 @@ export class ScreenSharingPeer extends Peer {
}
}
- private pushScreenSharingToRemoteUser() {
- const localScreenCapture: MediaStream | null = mediaManager.localScreenCapture;
- if(!localScreenCapture){
- return;
- }
-
- this.addStream(localScreenCapture);
- return;
- }
-
public stopPushingScreenSharingToRemoteUser(stream: MediaStream) {
this.removeStream(stream);
this.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_CONSTRAINT, streamEnded: true})));
diff --git a/front/src/WebRtc/SimplePeer.ts b/front/src/WebRtc/SimplePeer.ts
index 4633374d..9193f18b 100644
--- a/front/src/WebRtc/SimplePeer.ts
+++ b/front/src/WebRtc/SimplePeer.ts
@@ -15,7 +15,10 @@ import {connectionManager} from "../Connexion/ConnectionManager";
import {GameConnexionTypes} from "../Url/UrlManager";
import {blackListManager} from "./BlackListManager";
import {get} from "svelte/store";
-import {localStreamStore, obtainedMediaConstraintStore} from "../Stores/MediaStore";
+import {localStreamStore, LocalStreamStoreValue, obtainedMediaConstraintStore} from "../Stores/MediaStore";
+import {screenSharingLocalStreamStore} from "../Stores/ScreenSharingStore";
+import {DivImportance, layoutManager} from "./LayoutManager";
+import {HtmlUtils} from "./HtmlUtils";
export interface UserSimplePeerInterface{
userId: number;
@@ -39,9 +42,9 @@ export class SimplePeer {
private PeerScreenSharingConnectionArray: Map = new Map();
private PeerConnectionArray: Map = new Map();
- private readonly sendLocalVideoStreamCallback: UpdatedLocalStreamCallback;
private readonly sendLocalScreenSharingStreamCallback: StartScreenSharingCallback;
private readonly stopLocalScreenSharingStreamCallback: StopScreenSharingCallback;
+ private readonly unsubscribers: (() => void)[] = [];
private readonly peerConnectionListeners: Array = new Array();
private readonly userId: number;
private lastWebrtcUserName: string|undefined;
@@ -49,13 +52,32 @@ export class SimplePeer {
constructor(private Connection: RoomConnection, private enableReporting: boolean, private myName: string) {
// We need to go through this weird bound function pointer in order to be able to "free" this reference later.
- this.sendLocalVideoStreamCallback = this.sendLocalVideoStream.bind(this);
this.sendLocalScreenSharingStreamCallback = this.sendLocalScreenSharingStream.bind(this);
this.stopLocalScreenSharingStreamCallback = this.stopLocalScreenSharingStream.bind(this);
- mediaManager.onUpdateLocalStream(this.sendLocalVideoStreamCallback);
- mediaManager.onStartScreenSharing(this.sendLocalScreenSharingStreamCallback);
- mediaManager.onStopScreenSharing(this.stopLocalScreenSharingStreamCallback);
+ this.unsubscribers.push(localStreamStore.subscribe((streamResult) => {
+ this.sendLocalVideoStream(streamResult);
+ }));
+
+ let localScreenCapture: MediaStream|null = null;
+
+ this.unsubscribers.push(screenSharingLocalStreamStore.subscribe((streamResult) => {
+ if (streamResult.type === 'error') {
+ // Let's ignore screen sharing errors, we will deal with those in a different way.
+ return;
+ }
+
+ if (streamResult.stream !== null) {
+ localScreenCapture = streamResult.stream;
+ this.sendLocalScreenSharingStream(localScreenCapture);
+ } else {
+ if (localScreenCapture) {
+ this.stopLocalScreenSharingStream(localScreenCapture);
+ localScreenCapture = null;
+ }
+ }
+ }));
+
this.userId = Connection.getUserId();
this.initialise();
}
@@ -106,13 +128,19 @@ export class SimplePeer {
if(!user.initiator){
return;
}
- this.createPeerConnection(user);
+ const streamResult = get(localStreamStore);
+ let stream : MediaStream | null = null;
+ if (streamResult.type === 'success' && streamResult.stream) {
+ stream = streamResult.stream;
+ }
+
+ this.createPeerConnection(user, stream);
}
/**
* create peer connection to bind users
*/
- private createPeerConnection(user : UserSimplePeerInterface) : VideoPeer | null {
+ private createPeerConnection(user : UserSimplePeerInterface, localStream: MediaStream | null) : VideoPeer | null {
const peerConnection = this.PeerConnectionArray.get(user.userId)
if (peerConnection) {
if (peerConnection.destroyed) {
@@ -122,11 +150,11 @@ export class SimplePeer {
if (!peerConnexionDeleted) {
throw 'Error to delete peer connection';
}
- this.createPeerConnection(user);
+ //return this.createPeerConnection(user, localStream);
} else {
peerConnection.toClose = false;
+ return null;
}
- return null;
}
let name = user.name;
@@ -144,7 +172,7 @@ export class SimplePeer {
this.lastWebrtcUserName = user.webRtcUser;
this.lastWebrtcPassword = user.webRtcPassword;
- const peer = new VideoPeer(user, user.initiator ? user.initiator : false, this.Connection);
+ const peer = new VideoPeer(user, user.initiator ? user.initiator : false, this.Connection, localStream);
//permit to send message
mediaManager.addSendMessageCallback(user.userId,(message: string) => {
@@ -155,8 +183,9 @@ export class SimplePeer {
// When a connection is established to a video stream, and if a screen sharing is taking place,
// the user sharing screen should also initiate a connection to the remote user!
peer.on('connect', () => {
- if (mediaManager.localScreenCapture) {
- this.sendLocalScreenSharingStreamToUser(user.userId);
+ const streamResult = get(screenSharingLocalStreamStore);
+ if (streamResult.type === 'success' && streamResult.stream !== null) {
+ this.sendLocalScreenSharingStreamToUser(user.userId, streamResult.stream);
}
});
@@ -175,7 +204,7 @@ export class SimplePeer {
/**
* create peer connection to bind users
*/
- private createPeerScreenSharingConnection(user : UserSimplePeerInterface) : ScreenSharingPeer | null{
+ private createPeerScreenSharingConnection(user : UserSimplePeerInterface, stream: MediaStream | null) : ScreenSharingPeer | null{
const peerConnection = this.PeerScreenSharingConnectionArray.get(user.userId);
if(peerConnection){
if(peerConnection.destroyed){
@@ -185,7 +214,7 @@ export class SimplePeer {
if(!peerConnexionDeleted){
throw 'Error to delete peer connection';
}
- this.createPeerConnection(user);
+ this.createPeerConnection(user, stream);
}else {
peerConnection.toClose = false;
}
@@ -204,7 +233,7 @@ export class SimplePeer {
user.webRtcPassword = this.lastWebrtcPassword;
}
- const peer = new ScreenSharingPeer(user, user.initiator ? user.initiator : false, this.Connection);
+ const peer = new ScreenSharingPeer(user, user.initiator ? user.initiator : false, this.Connection, stream);
this.PeerScreenSharingConnectionArray.set(user.userId, peer);
for (const peerConnectionListener of this.peerConnectionListeners) {
@@ -234,7 +263,7 @@ export class SimplePeer {
const userIndex = this.Users.findIndex(user => user.userId === userId);
if(userIndex < 0){
- throw 'Couln\'t delete user';
+ throw 'Couldn\'t delete user';
} else {
this.Users.splice(userIndex, 1);
}
@@ -294,7 +323,9 @@ export class SimplePeer {
* Unregisters any held event handler.
*/
public unregister() {
- mediaManager.removeUpdateLocalStreamEventListener(this.sendLocalVideoStreamCallback);
+ for (const unsubscriber of this.unsubscribers) {
+ unsubscriber();
+ }
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -302,7 +333,13 @@ export class SimplePeer {
try {
//if offer type, create peer connection
if(data.signal.type === "offer"){
- this.createPeerConnection(data);
+ const streamResult = get(localStreamStore);
+ let stream : MediaStream | null = null;
+ if (streamResult.type === 'success' && streamResult.stream) {
+ stream = streamResult.stream;
+ }
+
+ this.createPeerConnection(data, stream);
}
const peer = this.PeerConnectionArray.get(data.userId);
if (peer !== undefined) {
@@ -318,18 +355,26 @@ export class SimplePeer {
private receiveWebrtcScreenSharingSignal(data: WebRtcSignalReceivedMessageInterface) {
if (blackListManager.isBlackListed(data.userId)) return;
console.log("receiveWebrtcScreenSharingSignal", data);
+ const streamResult = get(screenSharingLocalStreamStore);
+ let stream : MediaStream | null = null;
+ if (streamResult.type === 'success' && streamResult.stream !== null) {
+ stream = streamResult.stream;
+ }
+
try {
//if offer type, create peer connection
if(data.signal.type === "offer"){
- this.createPeerScreenSharingConnection(data);
+ this.createPeerScreenSharingConnection(data, stream);
}
const peer = this.PeerScreenSharingConnectionArray.get(data.userId);
if (peer !== undefined) {
peer.signal(data.signal);
} else {
console.error('Could not find peer whose ID is "'+data.userId+'" in receiveWebrtcScreenSharingSignal');
- console.info('tentative to create new peer connexion');
- this.sendLocalScreenSharingStreamToUser(data.userId);
+ console.info('Attempt to create new peer connexion');
+ if (stream) {
+ this.sendLocalScreenSharingStreamToUser(data.userId, stream);
+ }
}
} catch (e) {
console.error(`receiveWebrtcSignal => ${data.userId}`, e);
@@ -339,21 +384,19 @@ export class SimplePeer {
}
}
- private pushVideoToRemoteUser(userId : number) {
+ private pushVideoToRemoteUser(userId: number, streamResult: LocalStreamStoreValue) {
try {
const PeerConnection = this.PeerConnectionArray.get(userId);
if (!PeerConnection) {
throw new Error('While adding media, cannot find user with ID ' + userId);
}
- const result = get(localStreamStore);
+ PeerConnection.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_CONSTRAINT, ...streamResult.constraints})));
- PeerConnection.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_CONSTRAINT, ...result.constraints})));
-
- if (result.type === 'error') {
+ if (streamResult.type === 'error') {
return;
}
- const localStream: MediaStream | null = result.stream;
+ const localStream: MediaStream | null = streamResult.stream;
if(!localStream){
return;
@@ -370,15 +413,11 @@ export class SimplePeer {
}
}
- private pushScreenSharingToRemoteUser(userId : number) {
+ private pushScreenSharingToRemoteUser(userId: number, localScreenCapture: MediaStream) {
const PeerConnection = this.PeerScreenSharingConnectionArray.get(userId);
if (!PeerConnection) {
throw new Error('While pushing screen sharing, cannot find user with ID ' + userId);
}
- const localScreenCapture: MediaStream | null = mediaManager.localScreenCapture;
- if(!localScreenCapture){
- return;
- }
for (const track of localScreenCapture.getTracks()) {
PeerConnection.addTrack(track, localScreenCapture);
@@ -386,23 +425,18 @@ export class SimplePeer {
return;
}
- public sendLocalVideoStream(){
+ public sendLocalVideoStream(streamResult: LocalStreamStoreValue){
for (const user of this.Users) {
- this.pushVideoToRemoteUser(user.userId);
+ this.pushVideoToRemoteUser(user.userId, streamResult);
}
}
/**
* Triggered locally when clicking on the screen sharing button
*/
- public sendLocalScreenSharingStream() {
- if (!mediaManager.localScreenCapture) {
- console.error('Could not find localScreenCapture to share')
- return;
- }
-
+ public sendLocalScreenSharingStream(localScreenCapture: MediaStream) {
for (const user of this.Users) {
- this.sendLocalScreenSharingStreamToUser(user.userId);
+ this.sendLocalScreenSharingStreamToUser(user.userId, localScreenCapture);
}
}
@@ -415,11 +449,11 @@ export class SimplePeer {
}
}
- private sendLocalScreenSharingStreamToUser(userId: number): void {
+ private sendLocalScreenSharingStreamToUser(userId: number, localScreenCapture: MediaStream): void {
if (blackListManager.isBlackListed(userId)) return;
// If a connection already exists with user (because it is already sharing a screen with us... let's use this connection)
if (this.PeerScreenSharingConnectionArray.has(userId)) {
- this.pushScreenSharingToRemoteUser(userId);
+ this.pushScreenSharingToRemoteUser(userId, localScreenCapture);
return;
}
@@ -427,7 +461,7 @@ export class SimplePeer {
userId,
initiator: true
};
- const PeerConnectionScreenSharing = this.createPeerScreenSharingConnection(screenSharingUser);
+ const PeerConnectionScreenSharing = this.createPeerScreenSharingConnection(screenSharingUser, localScreenCapture);
if (!PeerConnectionScreenSharing) {
return;
}
diff --git a/front/src/WebRtc/VideoPeer.ts b/front/src/WebRtc/VideoPeer.ts
index 32e8e97f..5ca8952c 100644
--- a/front/src/WebRtc/VideoPeer.ts
+++ b/front/src/WebRtc/VideoPeer.ts
@@ -27,7 +27,7 @@ export class VideoPeer extends Peer {
private onBlockSubscribe: Subscription;
private onUnBlockSubscribe: Subscription;
- constructor(public user: UserSimplePeerInterface, initiator: boolean, private connection: RoomConnection) {
+ constructor(public user: UserSimplePeerInterface, initiator: boolean, private connection: RoomConnection, localStream: MediaStream | null) {
super({
initiator: initiator ? initiator : false,
//reconnectTimer: 10000,
@@ -107,7 +107,7 @@ export class VideoPeer extends Peer {
this._onFinish();
});
- this.pushVideoToRemoteUser();
+ this.pushVideoToRemoteUser(localStream);
this.onBlockSubscribe = blackListManager.onBlockStream.subscribe((userId) => {
if (userId === this.userId) {
this.toggleRemoteStream(false);
@@ -190,9 +190,8 @@ export class VideoPeer extends Peer {
}
}
- private pushVideoToRemoteUser() {
+ private pushVideoToRemoteUser(localStream: MediaStream | null) {
try {
- const localStream: MediaStream | null = mediaManager.localStream;
this.write(new Buffer(JSON.stringify({type: MESSAGE_TYPE_CONSTRAINT, ...get(obtainedMediaConstraintStore)})));
if(!localStream){
diff --git a/front/src/index.ts b/front/src/index.ts
index f9017c24..90d4c612 100644
--- a/front/src/index.ts
+++ b/front/src/index.ts
@@ -13,7 +13,6 @@ import WebFontLoaderPlugin from 'phaser3-rex-plugins/plugins/webfontloader-plugi
import {EntryScene} from "./Phaser/Login/EntryScene";
import {coWebsiteManager} from "./WebRtc/CoWebsiteManager";
import {MenuScene} from "./Phaser/Menu/MenuScene";
-import {HelpCameraSettingsScene} from "./Phaser/Menu/HelpCameraSettingsScene";
import {localUserStore} from "./Connexion/LocalUserStore";
import {ErrorScene} from "./Phaser/Reconnecting/ErrorScene";
import {iframeListener} from "./Api/IframeListener";
@@ -96,7 +95,7 @@ const config: GameConfig = {
ErrorScene,
CustomizeScene,
MenuScene,
- HelpCameraSettingsScene],
+ ],
//resolution: window.devicePixelRatio / 2,
fps: fps,
dom: {
@@ -152,7 +151,9 @@ iframeListener.init();
const app = new App({
target: HtmlUtils.getElementByIdOrFail('svelte-overlay'),
- props: { },
+ props: {
+ game: game
+ },
})
export default app
diff --git a/front/style/fonts.scss b/front/style/fonts.scss
index 5ef9b9b4..a49d3967 100644
--- a/front/style/fonts.scss
+++ b/front/style/fonts.scss
@@ -1 +1,9 @@
-@import "~@fontsource/press-start-2p/index.css";
\ No newline at end of file
+@import "~@fontsource/press-start-2p/index.css";
+
+*{
+ font-family: PixelFont-7,monospace;
+}
+
+.nes-btn {
+ font-family: "Press Start 2P";
+}
diff --git a/front/style/index.scss b/front/style/index.scss
index 47f13c3b..7ed141cd 100644
--- a/front/style/index.scss
+++ b/front/style/index.scss
@@ -1,5 +1,5 @@
@import "cowebsite.scss";
@import "cowebsite-mobile.scss";
-@import "style.css";
+@import "style";
@import "mobile-style.scss";
-@import "fonts.scss";
\ No newline at end of file
+@import "fonts.scss";
diff --git a/front/style/style.css b/front/style/style.scss
similarity index 95%
rename from front/style/style.css
rename to front/style/style.scss
index 13c2331b..6d439bac 100644
--- a/front/style/style.css
+++ b/front/style/style.scss
@@ -136,6 +136,7 @@ body .message-info.warning{
.video-container.div-myCamVideo{
border: none;
+ background-color: transparent;
}
.div-myCamVideo {
@@ -152,6 +153,8 @@ body .message-info.warning{
}
video.myCamVideo{
+ background-color: #00000099;
+ max-height: 20vh;
width: 15vw;
-webkit-transform: scaleX(-1);
transform: scaleX(-1);
@@ -363,39 +366,6 @@ video.myCamVideo{
}
}
-.webrtcsetup{
- display: none;
- position: absolute;
- top: 140px;
- left: 0;
- right: 0;
- margin-left: auto;
- margin-right: auto;
- height: 50%;
- width: 50%;
- border: white 6px solid;
-}
-.webrtcsetup .background-img {
- position: relative;
- display: block;
- width: 40%;
- height: 60%;
- margin-left: auto;
- margin-right: auto;
- top: 50%;
- transform: translateY(-50%);
-}
-#myCamVideoSetup {
- width: 100%;
- height: 100%;
- -webkit-transform: scaleX(-1);
- transform: scaleX(-1);
-}
-.webrtcsetup.active{
- display: block;
-}
-
-
/* New layout */
body {
margin: 0;
@@ -909,35 +879,6 @@ input[type=range]:focus::-ms-fill-upper {
}
-
-/*audio html when audio message playing*/
-.main-container .audio-playing {
- position: absolute;
- width: 200px;
- height: 54px;
- right: -210px;
- top: 40px;
- transition: all 0.1s ease-out;
- background-color: black;
- border-radius: 30px 0 0 30px;
- display: inline-flex;
-}
-
-.main-container .audio-playing.active{
- right: 0;
-}
-.main-container .audio-playing img{
- /*width: 30px;*/
- border-radius: 50%;
- background-color: #ffda01;
- padding: 10px;
-}
-.main-container .audio-playing p{
- color: white;
- margin-left: 10px;
- margin-top: 14px;
-}
-
/* VIDEO QUALITY */
.main-console div.setting h1{
color: white;
@@ -1313,4 +1254,22 @@ div.action.danger p.action-body{
width: 100%;
height: 100%;
pointer-events: none;
+
+ & > div {
+ position: relative;
+ width: 100%;
+ height: 100%;
+
+ & > div {
+ position: absolute;
+ width: 100%;
+ height: 100%;
+ }
+
+ & > div.scrollable {
+ overflow-y: auto;
+ -webkit-overflow-scrolling: touch;
+ pointer-events: auto;
+ }
+ }
}
diff --git a/front/webpack.config.ts b/front/webpack.config.ts
index 6ebf907f..3a69b74a 100644
--- a/front/webpack.config.ts
+++ b/front/webpack.config.ts
@@ -7,6 +7,7 @@ import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import sveltePreprocess from 'svelte-preprocess';
import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";
import NodePolyfillPlugin from 'node-polyfill-webpack-plugin';
+import {DISPLAY_TERMS_OF_USE} from "./src/Enum/EnvironmentVariable";
const mode = process.env.NODE_ENV ?? 'development';
const isProduction = mode === 'production';
@@ -88,7 +89,16 @@ module.exports = {
preprocess: sveltePreprocess({
scss: true,
sass: true,
- })
+ }),
+ onwarn: function (warning: { code: string }, handleWarning: (warning: { code: string }) => void) {
+ // See https://github.com/sveltejs/svelte/issues/4946#issuecomment-662168782
+
+ if (warning.code === 'a11y-no-onchange') { return }
+ if (warning.code === 'a11y-autofocus') { return }
+
+ // process as usual
+ handleWarning(warning);
+ }
}
}
},
@@ -175,7 +185,8 @@ module.exports = {
'JITSI_PRIVATE_MODE': null,
'START_ROOM_URL': null,
'MAX_USERNAME_LENGTH': 8,
- 'MAX_PER_GROUP': 4
+ 'MAX_PER_GROUP': 4,
+ 'DISPLAY_TERMS_OF_USE': false,
})
],
diff --git a/front/yarn.lock b/front/yarn.lock
index c06e8a04..fde26272 100644
--- a/front/yarn.lock
+++ b/front/yarn.lock
@@ -30,6 +30,13 @@
chalk "^2.0.0"
js-tokens "^4.0.0"
+"@babel/runtime@^7.14.0":
+ version "7.14.0"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.0.tgz#46794bc20b612c5f75e62dd071e24dfd95f1cbe6"
+ integrity sha512-JELkvo/DlpNdJ7dlyw/eY7E0suy5i5GQH+Vlxaq1nsNJ+H7f4Vtv3jMeCEgRhZZQFXTjldYfQgv2qmM6M1v5wA==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
"@discoveryjs/json-ext@^0.5.0":
version "0.5.3"
resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz#90420f9f9c6d3987f176a19a7d8e764271a2f55d"
@@ -783,6 +790,14 @@ atob@^2.1.2:
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
+automation-events@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/automation-events/-/automation-events-4.0.1.tgz#93acef8a457cbea65f16fdcef8f349fd2fafe298"
+ integrity sha512-8bQx+PVtNDMD5F2H40cQs7oexZve3Z0xC9fWRQT4fltF65f/WsSpjM4jpulL4K4yLLB71oi4/xVJJCJ5I/Kjbw==
+ dependencies:
+ "@babel/runtime" "^7.14.0"
+ tslib "^2.2.0"
+
available-typed-arrays@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.2.tgz#6b098ca9d8039079ee3f77f7b783c4480ba513f5"
@@ -4331,6 +4346,11 @@ rechoir@^0.7.0:
dependencies:
resolve "^1.9.0"
+regenerator-runtime@^0.13.4:
+ version "0.13.7"
+ resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55"
+ integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==
+
regex-not@^1.0.0, regex-not@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
@@ -4895,6 +4915,15 @@ sprintf-js@~1.0.2:
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
+standardized-audio-context@^25.2.4:
+ version "25.2.4"
+ resolved "https://registry.yarnpkg.com/standardized-audio-context/-/standardized-audio-context-25.2.4.tgz#d64dbdd70615171ec90d1b7151a0d945af94cf3d"
+ integrity sha512-uQKZXRnXrPVO1V6SwZ7PiV3RkQqRY3n7i6Q8nbTXYvoz8NftRNzfOIlwefpuC8LVLUUs9dhbKTpP+WOA82dkBw==
+ dependencies:
+ "@babel/runtime" "^7.14.0"
+ automation-events "^4.0.1"
+ tslib "^2.2.0"
+
static-extend@^0.1.1:
version "0.1.2"
resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6"
@@ -5225,7 +5254,7 @@ tslib@^1.8.1, tslib@^1.9.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
-tslib@^2.0.3:
+tslib@^2.0.3, tslib@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.2.0.tgz#fb2c475977e35e241311ede2693cee1ec6698f5c"
integrity sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w==
@@ -5639,9 +5668,9 @@ wrappy@1:
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
ws@^6.2.1:
- version "6.2.1"
- resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb"
- integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==
+ version "6.2.2"
+ resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e"
+ integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw==
dependencies:
async-limiter "~1.0.0"
diff --git a/maps/tests/help_camera_setting.json b/maps/tests/help_camera_setting.json
new file mode 100644
index 00000000..2dcdec3a
--- /dev/null
+++ b/maps/tests/help_camera_setting.json
@@ -0,0 +1,164 @@
+{ "compressionlevel":-1,
+ "height":10,
+ "infinite":false,
+ "layers":[
+ {
+ "data":[33, 34, 34, 34, 34, 34, 34, 34, 34, 35, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 49, 50, 50, 50, 50, 50, 50, 50, 50, 51],
+ "height":10,
+ "id":3,
+ "name":"bottom",
+ "opacity":1,
+ "type":"tilelayer",
+ "visible":true,
+ "width":10,
+ "x":0,
+ "y":0
+ },
+ {
+ "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ "height":10,
+ "id":1,
+ "name":"start",
+ "opacity":1,
+ "type":"tilelayer",
+ "visible":true,
+ "width":10,
+ "x":0,
+ "y":0
+ },
+ {
+ "draworder":"topdown",
+ "id":2,
+ "name":"floorLayer",
+ "objects":[
+ {
+ "height":254.57168784029,
+ "id":1,
+ "name":"",
+ "rotation":0,
+ "text":
+ {
+ "fontfamily":"Sans Serif",
+ "pixelsize":12,
+ "text":"Test 1 : \nBlock permission to camera and\/or microphone access.\n\nResult 1 :\nOrange popup show at the bottom of the screen.\nIf you click on it, the HelpCameraSetting popup open.\n\nTest 2 : \nReload the page and block permission to camera and\/or microphone access on the camera setting page.\n\nResult 2 : \nOrange popup show at the bottom of the screen.\nIf you click on it, the HelpCameraSetting popup open.\n",
+ "wrap":true
+ },
+ "type":"",
+ "visible":true,
+ "width":295.278811252269,
+ "x":12.2517014519056,
+ "y":49.3021778584392
+ }],
+ "opacity":1,
+ "type":"objectgroup",
+ "visible":true,
+ "x":0,
+ "y":0
+ }],
+ "nextlayerid":6,
+ "nextobjectid":2,
+ "orientation":"orthogonal",
+ "renderorder":"right-down",
+ "tiledversion":"1.4.3",
+ "tileheight":32,
+ "tilesets":[
+ {
+ "columns":8,
+ "firstgid":1,
+ "image":"Validation\/tileset_dungeon.png",
+ "imageheight":256,
+ "imagewidth":256,
+ "margin":0,
+ "name":"dungeon",
+ "spacing":0,
+ "tilecount":64,
+ "tileheight":32,
+ "tiles":[
+ {
+ "id":0,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":1,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":2,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":5,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":8,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":10,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":16,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":17,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ },
+ {
+ "id":18,
+ "properties":[
+ {
+ "name":"collides",
+ "type":"bool",
+ "value":true
+ }]
+ }],
+ "tilewidth":32
+ }],
+ "tilewidth":32,
+ "type":"map",
+ "version":1.4,
+ "width":10
+}
\ No newline at end of file
diff --git a/maps/tests/index.html b/maps/tests/index.html
index a17a3b5d..9c95c281 100644
--- a/maps/tests/index.html
+++ b/maps/tests/index.html
@@ -82,6 +82,14 @@
Test energy consumption
+
+
+ Success Failure Pending
+
+
+ Test the HelpCameraSettingScene
+
+