From de5a505296fa4b4f51ce6cf7f13c3894f656bbec Mon Sep 17 00:00:00 2001 From: Lukas Hass Date: Thu, 3 Feb 2022 19:26:25 +0100 Subject: [PATCH 01/42] remove explicit definition of prettier-plugin-svelte fixes prettier in vscode --- front/.prettierrc.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/front/.prettierrc.json b/front/.prettierrc.json index 057ed062..e8980d15 100644 --- a/front/.prettierrc.json +++ b/front/.prettierrc.json @@ -1,5 +1,4 @@ { "printWidth": 120, - "tabWidth": 4, - "plugins": ["prettier-plugin-svelte"] + "tabWidth": 4 } From 6863fa37640a179ed314f28afeee267ead80ffae Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Fri, 4 Feb 2022 14:46:57 +0100 Subject: [PATCH 02/42] Fix game unfocusable after clicking on iframe --- front/src/Phaser/Game/Game.ts | 9 --------- front/src/Phaser/UserInput/UserInputManager.ts | 5 +++++ 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/front/src/Phaser/Game/Game.ts b/front/src/Phaser/Game/Game.ts index 865026f7..783f2348 100644 --- a/front/src/Phaser/Game/Game.ts +++ b/front/src/Phaser/Game/Game.ts @@ -26,15 +26,6 @@ export class Game extends Phaser.Game { } } }); - - /*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/UserInput/UserInputManager.ts b/front/src/Phaser/UserInput/UserInputManager.ts index ffa67c3a..112ab0e5 100644 --- a/front/src/Phaser/UserInput/UserInputManager.ts +++ b/front/src/Phaser/UserInput/UserInputManager.ts @@ -255,6 +255,11 @@ export class UserInputManager { (pointer: Phaser.Input.Pointer, gameObjects: Phaser.GameObjects.GameObject[]) => { this.joystick?.hide(); this.userInputHandler.handlePointerUpEvent(pointer, gameObjects); + + // Disable focus on iframe (need by Firefox) + if (pointer.downElement.nodeName === "CANVAS" && document.activeElement instanceof HTMLIFrameElement) { + document.activeElement.blur(); + } } ); From 72b4438d1ed7d59d31ab8020b396a15dfcd97fc9 Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Mon, 7 Feb 2022 10:39:03 +0100 Subject: [PATCH 03/42] outline color is decided from particular system level --- front/src/Phaser/Entity/Character.ts | 8 +++---- front/src/Phaser/Game/ActivatablesManager.ts | 8 ++++--- front/src/Phaser/Game/GameScene.ts | 6 +++++ front/src/Phaser/Game/OutlineableInterface.ts | 4 ++-- front/src/Stores/OutlineColorStore.ts | 24 +++++++++---------- 5 files changed, 28 insertions(+), 22 deletions(-) diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index 5ec031bd..c26b87ba 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -455,16 +455,16 @@ export abstract class Character extends Container implements OutlineableInterfac this.outlineColorStore.removeApiColor(); } - public pointerOverOutline(): void { - this.outlineColorStore.pointerOver(); + public pointerOverOutline(color: number): void { + this.outlineColorStore.pointerOver(color); } public pointerOutOutline(): void { this.outlineColorStore.pointerOut(); } - public characterCloseByOutline(): void { - this.outlineColorStore.characterCloseBy(); + public characterCloseByOutline(color: number): void { + this.outlineColorStore.characterCloseBy(color); } public characterFarAwayOutline(): void { diff --git a/front/src/Phaser/Game/ActivatablesManager.ts b/front/src/Phaser/Game/ActivatablesManager.ts index 60e967d9..90f1c8d3 100644 --- a/front/src/Phaser/Game/ActivatablesManager.ts +++ b/front/src/Phaser/Game/ActivatablesManager.ts @@ -11,6 +11,8 @@ export class ActivatablesManager { private currentPlayer: Player; + private readonly outlineColor = 0xffff00; + constructor(currentPlayer: Player) { this.currentPlayer = currentPlayer; } @@ -27,7 +29,7 @@ export class ActivatablesManager { } this.selectedActivatableObjectByPointer = object; if (isOutlineable(this.selectedActivatableObjectByPointer)) { - this.selectedActivatableObjectByPointer?.pointerOverOutline(); + this.selectedActivatableObjectByPointer?.pointerOverOutline(this.outlineColor); } } @@ -37,7 +39,7 @@ export class ActivatablesManager { } this.selectedActivatableObjectByPointer = undefined; if (isOutlineable(this.selectedActivatableObjectByDistance)) { - this.selectedActivatableObjectByDistance?.characterCloseByOutline(); + this.selectedActivatableObjectByDistance?.characterCloseByOutline(this.outlineColor); } } @@ -60,7 +62,7 @@ export class ActivatablesManager { } this.selectedActivatableObjectByDistance = newNearestObject; if (isOutlineable(this.selectedActivatableObjectByDistance)) { - this.selectedActivatableObjectByDistance?.characterCloseByOutline(); + this.selectedActivatableObjectByDistance?.characterCloseByOutline(this.outlineColor); } } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 242d97c8..e7242f90 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1737,6 +1737,12 @@ ${escapedMessage} emoteMenuStore.openEmoteMenu(); } }); + this.CurrentPlayer.on(Phaser.Input.Events.POINTER_OVER, (pointer: Phaser.Input.Pointer) => { + this.CurrentPlayer.pointerOverOutline(0x00ffff); + }); + this.CurrentPlayer.on(Phaser.Input.Events.POINTER_OUT, (pointer: Phaser.Input.Pointer) => { + this.CurrentPlayer.pointerOutOutline(); + }); this.CurrentPlayer.on(requestEmoteEventName, (emoteKey: string) => { this.connection?.emitEmoteEvent(emoteKey); analyticsClient.launchEmote(emoteKey); diff --git a/front/src/Phaser/Game/OutlineableInterface.ts b/front/src/Phaser/Game/OutlineableInterface.ts index bee560cc..7112fe84 100644 --- a/front/src/Phaser/Game/OutlineableInterface.ts +++ b/front/src/Phaser/Game/OutlineableInterface.ts @@ -3,8 +3,8 @@ export interface OutlineableInterface { removeFollowOutlineColor(): void; setApiOutlineColor(color: number): void; removeApiOutlineColor(): void; - pointerOverOutline(): void; + pointerOverOutline(color: number): void; pointerOutOutline(): void; - characterCloseByOutline(): void; + characterCloseByOutline(color: number): void; characterFarAwayOutline(): void; } diff --git a/front/src/Stores/OutlineColorStore.ts b/front/src/Stores/OutlineColorStore.ts index a35cc9c9..a444037d 100644 --- a/front/src/Stores/OutlineColorStore.ts +++ b/front/src/Stores/OutlineColorStore.ts @@ -5,38 +5,36 @@ export function createColorStore() { let followColor: number | undefined = undefined; let apiColor: number | undefined = undefined; + let pointedByPointer: number | undefined = undefined; + let pointedByCharacter: number | undefined = undefined; - let pointedByPointer: boolean = false; - let pointedByCharacter: boolean = false; const updateColor = () => { - if (pointedByPointer || pointedByCharacter) { - set(0xffff00); - } else { - set(followColor ?? apiColor); - } + console.log('update color'); + console.log(pointedByPointer, pointedByCharacter, followColor, apiColor); + set(pointedByPointer ?? pointedByCharacter ?? followColor ?? apiColor); }; return { subscribe, - pointerOver() { - pointedByPointer = true; + pointerOver(color: number) { + pointedByPointer = color; updateColor(); }, pointerOut() { - pointedByPointer = false; + pointedByPointer = undefined; updateColor(); }, - characterCloseBy() { - pointedByCharacter = true; + characterCloseBy(color: number) { + pointedByCharacter = color; updateColor(); }, characterFarAway() { - pointedByCharacter = false; + pointedByCharacter = undefined; updateColor(); }, From bf0d2eb4124a16895363e0a600411a47f088517a Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Mon, 7 Feb 2022 11:08:52 +0100 Subject: [PATCH 04/42] directional shift for current player when trying to activate entities --- front/src/Phaser/Entity/Character.ts | 13 +++++++++++++ front/src/Phaser/Game/ActivatablesManager.ts | 8 ++++++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index c26b87ba..061bc2e5 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -159,6 +159,19 @@ export abstract class Character extends Container implements OutlineableInterfac return { x: this.x, y: this.y }; } + /** + * Returns position based on where player is currently facing + * @param shift How far from player should the point of interest be. + */ + public getDirectionalActivationPosition(shift: number): { x: number, y: number } { + switch (this.lastDirection) { + case PlayerAnimationDirections.Down: { return { x: this.x, y: this.y + shift }; } + case PlayerAnimationDirections.Left: { return { x: this.x - shift, y: this.y }; } + case PlayerAnimationDirections.Right: { return { x: this.x + shift, y: this.y }; } + case PlayerAnimationDirections.Up: { return { x: this.x, y: this.y - shift }; } + } + } + public getObjectToOutline(): Phaser.GameObjects.GameObject { return this.playerNameText; } diff --git a/front/src/Phaser/Game/ActivatablesManager.ts b/front/src/Phaser/Game/ActivatablesManager.ts index 90f1c8d3..f5f893db 100644 --- a/front/src/Phaser/Game/ActivatablesManager.ts +++ b/front/src/Phaser/Game/ActivatablesManager.ts @@ -12,6 +12,7 @@ export class ActivatablesManager { private currentPlayer: Player; private readonly outlineColor = 0xffff00; + private readonly directionalActivationPositionShift = 50; constructor(currentPlayer: Player) { this.currentPlayer = currentPlayer; @@ -79,7 +80,7 @@ export class ActivatablesManager { return closestObject; } public updateActivatableObjectsDistances(objects: ActivatableInterface[]): void { - const currentPlayerPos = this.currentPlayer.getPosition(); + const currentPlayerPos = this.currentPlayer.getDirectionalActivationPosition(this.directionalActivationPositionShift); for (const object of objects) { const distance = MathUtils.distanceBetween(currentPlayerPos, object.getPosition()); this.activatableObjectsDistances.set(object, distance); @@ -89,7 +90,10 @@ export class ActivatablesManager { public updateDistanceForSingleActivatableObject(object: ActivatableInterface): void { this.activatableObjectsDistances.set( object, - MathUtils.distanceBetween(this.currentPlayer.getPosition(), object.getPosition()) + MathUtils.distanceBetween( + this.currentPlayer.getDirectionalActivationPosition(this.directionalActivationPositionShift), + object.getPosition(), + ) ); } } From 9571a52f1e674f5c820a3f5dc203783e95069d9a Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Mon, 7 Feb 2022 11:21:04 +0100 Subject: [PATCH 05/42] Re-implement action message on cowebsite trigger --- .../Phaser/Game/GameMapPropertiesListener.ts | 114 +++++++++++++++--- front/src/WebRtc/CoWebsiteManager.ts | 2 +- front/src/WebRtc/LayoutManager.ts | 1 + 3 files changed, 98 insertions(+), 19 deletions(-) diff --git a/front/src/Phaser/Game/GameMapPropertiesListener.ts b/front/src/Phaser/Game/GameMapPropertiesListener.ts index caa83cb0..900a00b6 100644 --- a/front/src/Phaser/Game/GameMapPropertiesListener.ts +++ b/front/src/Phaser/Game/GameMapPropertiesListener.ts @@ -6,7 +6,7 @@ import { coWebsiteManager } from "../../WebRtc/CoWebsiteManager"; import { layoutManagerActionStore } from "../../Stores/LayoutManagerStore"; import { localUserStore } from "../../Connexion/LocalUserStore"; import { get } from "svelte/store"; -import { ON_ACTION_TRIGGER_BUTTON } from "../../WebRtc/LayoutManager"; +import { ON_ACTION_TRIGGER_BUTTON, ON_ICON_TRIGGER_BUTTON } from "../../WebRtc/LayoutManager"; import type { ITiledMapLayer } from "../Map/ITiledMap"; import { GameMapProperties } from "./GameMapProperties"; import { highlightedEmbedScreen } from "../../Stores/EmbedScreensStore"; @@ -18,12 +18,14 @@ enum OpenCoWebsiteState { } interface OpenCoWebsite { - coWebsite: CoWebsite; + actionId: string; + coWebsite?: CoWebsite; state: OpenCoWebsiteState; } export class GameMapPropertiesListener { private coWebsitesOpenByLayer = new Map(); + private coWebsitesActionTriggerByLayer = new Map(); constructor(private scene: GameScene, private gameMap: GameMap) {} @@ -95,27 +97,19 @@ export class GameMapPropertiesListener { return; } - const actionUuid = "openWebsite-" + (Math.random() + 1).toString(36).substring(7); + const actionId = "openWebsite-" + (Math.random() + 1).toString(36).substring(7); if (this.coWebsitesOpenByLayer.has(layer)) { return; } - const coWebsite = coWebsiteManager.addCoWebsite( - openWebsiteProperty, - this.scene.MapUrlFile, - allowApiProperty, - websitePolicyProperty, - websitePositionProperty, - false - ); - this.coWebsitesOpenByLayer.set(layer, { - coWebsite: coWebsite, + actionId: actionId, + coWebsite: undefined, state: OpenCoWebsiteState.ASLEEP, }); - const openWebsiteFunction = () => { + const loadCoWebsiteFunction = (coWebsite: CoWebsite) => { coWebsiteManager .loadCoWebsite(coWebsite) .then((coWebsite) => { @@ -125,8 +119,10 @@ export class GameMapPropertiesListener { console.error("Error during a co-website closing"); }); this.coWebsitesOpenByLayer.delete(layer); + this.coWebsitesActionTriggerByLayer.delete(layer); } else { this.coWebsitesOpenByLayer.set(layer, { + actionId, coWebsite, state: OpenCoWebsiteState.OPENED, }); @@ -136,14 +132,74 @@ export class GameMapPropertiesListener { console.error("Error during loading a co-website: " + coWebsite.url); }); - layoutManagerActionStore.removeAction(actionUuid); + layoutManagerActionStore.removeAction(actionId); + }; + + const openCoWebsiteFunction = ( + url: string | undefined, + allowApi: boolean | undefined, + policy: string | undefined, + position: number | undefined + ) => { + const coWebsite = coWebsiteManager.addCoWebsite( + url ?? "", + this.scene.MapUrlFile, + allowApiProperty, + websitePolicyProperty, + websitePositionProperty, + false + ); + + loadCoWebsiteFunction(coWebsite); }; if ( - !localUserStore.getForceCowebsiteTrigger() && - websiteTriggerProperty !== ON_ACTION_TRIGGER_BUTTON + localUserStore.getForceCowebsiteTrigger() || + websiteTriggerProperty === ON_ACTION_TRIGGER_BUTTON ) { - openWebsiteFunction(); + if (!websiteTriggerMessageProperty) { + websiteTriggerMessageProperty = "Press SPACE or touch here to open web site"; + } + + this.coWebsitesActionTriggerByLayer.set(layer, actionId); + + layoutManagerActionStore.addAction({ + uuid: actionId, + type: "message", + message: websiteTriggerMessageProperty, + callback: () => + openCoWebsiteFunction( + openWebsiteProperty, + allowApiProperty, + websitePolicyProperty, + websitePositionProperty + ), + userInputManager: this.scene.userInputManager, + }); + } else if (!websiteTriggerProperty || websiteTriggerProperty === ON_ICON_TRIGGER_BUTTON) { + const coWebsite = coWebsiteManager.addCoWebsite( + openWebsiteProperty, + this.scene.MapUrlFile, + allowApiProperty, + websitePolicyProperty, + websitePositionProperty, + false + ); + + const ObjectByLayer = this.coWebsitesOpenByLayer.get(layer); + + if (ObjectByLayer) { + ObjectByLayer.coWebsite = coWebsite; + } + } + + if (!websiteTriggerProperty) { + openCoWebsiteFunction( + openWebsiteProperty, + allowApiProperty, + websitePolicyProperty, + websitePositionProperty + ); } }); }; @@ -192,6 +248,28 @@ export class GameMapPropertiesListener { } this.coWebsitesOpenByLayer.delete(layer); + + if (!websiteTriggerProperty) { + return; + } + + const actionStore = get(layoutManagerActionStore); + const actionTriggerUuid = this.coWebsitesActionTriggerByLayer.get(layer); + + if (!actionTriggerUuid) { + return; + } + + const action = + actionStore && actionStore.length > 0 + ? actionStore.find((action) => action.uuid === actionTriggerUuid) + : undefined; + + if (action) { + layoutManagerActionStore.removeAction(actionTriggerUuid); + } + + this.coWebsitesActionTriggerByLayer.delete(layer); }); }; diff --git a/front/src/WebRtc/CoWebsiteManager.ts b/front/src/WebRtc/CoWebsiteManager.ts index 8e646d9d..9f4f0c36 100644 --- a/front/src/WebRtc/CoWebsiteManager.ts +++ b/front/src/WebRtc/CoWebsiteManager.ts @@ -21,7 +21,7 @@ const gameOverlayDomId = "game-overlay"; const cowebsiteBufferDomId = "cowebsite-buffer"; // the id of the container who contains cowebsite iframes. const cowebsiteAsideHolderDomId = "cowebsite-aside-holder"; const cowebsiteLoaderDomId = "cowebsite-loader"; -export const cowebsiteCloseButtonId = "cowebsite-close"; +const cowebsiteCloseButtonId = "cowebsite-close"; const cowebsiteFullScreenButtonId = "cowebsite-fullscreen"; const cowebsiteOpenFullScreenImageId = "cowebsite-fullscreen-open"; const cowebsiteCloseFullScreenImageId = "cowebsite-fullscreen-close"; diff --git a/front/src/WebRtc/LayoutManager.ts b/front/src/WebRtc/LayoutManager.ts index de79047f..a926cb41 100644 --- a/front/src/WebRtc/LayoutManager.ts +++ b/front/src/WebRtc/LayoutManager.ts @@ -13,5 +13,6 @@ export enum DivImportance { } export const ON_ACTION_TRIGGER_BUTTON = "onaction"; +export const ON_ICON_TRIGGER_BUTTON = "onicon"; export type Box = { xStart: number; yStart: number; xEnd: number; yEnd: number }; From aec7790875e9802e494c88168c32d198d06a8d8b Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Mon, 7 Feb 2022 12:36:33 +0100 Subject: [PATCH 06/42] disable activation by distance if in JITSI --- front/src/Phaser/Game/ActivatablesManager.ts | 46 ++++++++++++++----- front/src/Phaser/Game/GameScene.ts | 2 + .../UserInput/GameSceneUserInputHandler.ts | 5 +- front/src/Stores/OutlineColorStore.ts | 2 - 4 files changed, 39 insertions(+), 16 deletions(-) diff --git a/front/src/Phaser/Game/ActivatablesManager.ts b/front/src/Phaser/Game/ActivatablesManager.ts index f5f893db..f136de7c 100644 --- a/front/src/Phaser/Game/ActivatablesManager.ts +++ b/front/src/Phaser/Game/ActivatablesManager.ts @@ -11,6 +11,8 @@ export class ActivatablesManager { private currentPlayer: Player; + private canSelectByDistance: boolean = true; + private readonly outlineColor = 0xffff00; private readonly directionalActivationPositionShift = 50; @@ -49,6 +51,9 @@ export class ActivatablesManager { } public deduceSelectedActivatableObjectByDistance(): void { + if (!this.canSelectByDistance) { + return; + } const newNearestObject = this.findNearestActivatableObject(); if (this.selectedActivatableObjectByDistance === newNearestObject) { return; @@ -67,18 +72,6 @@ export class ActivatablesManager { } } - private findNearestActivatableObject(): ActivatableInterface | undefined { - let shortestDistance: number = Infinity; - let closestObject: ActivatableInterface | undefined = undefined; - - for (const [object, distance] of this.activatableObjectsDistances.entries()) { - if (object.isActivatable() && object.activationRadius > distance && shortestDistance > distance) { - shortestDistance = distance; - closestObject = object; - } - } - return closestObject; - } public updateActivatableObjectsDistances(objects: ActivatableInterface[]): void { const currentPlayerPos = this.currentPlayer.getDirectionalActivationPosition(this.directionalActivationPositionShift); for (const object of objects) { @@ -96,4 +89,33 @@ export class ActivatablesManager { ) ); } + + public disableSelectingByDistance(): void { + this.canSelectByDistance = false; + if (isOutlineable(this.selectedActivatableObjectByDistance)) { + this.selectedActivatableObjectByDistance?.characterFarAwayOutline(); + } + this.selectedActivatableObjectByDistance = undefined; + } + + public enableSelectingByDistance(): void { + this.canSelectByDistance = true; + } + + private findNearestActivatableObject(): ActivatableInterface | undefined { + let shortestDistance: number = Infinity; + let closestObject: ActivatableInterface | undefined = undefined; + + for (const [object, distance] of this.activatableObjectsDistances.entries()) { + if (object.isActivatable() && object.activationRadius > distance && shortestDistance > distance) { + shortestDistance = distance; + closestObject = object; + } + } + return closestObject; + } + + public isSelectingByDistanceEnabled(): boolean { + return this.canSelectByDistance; + } } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index e7242f90..0f081c14 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -957,9 +957,11 @@ export class GameScene extends DirtyScene { this.gameMap.onPropertyChange(GameMapProperties.JITSI_ROOM, (newValue, oldValue, allProps) => { if (newValue === undefined) { + this.activatablesManager.enableSelectingByDistance(); layoutManagerActionStore.removeAction("jitsi"); this.stopJitsi(); } else { + this.activatablesManager.disableSelectingByDistance(); const openJitsiRoomFunction = () => { const roomName = jitsiFactory.getRoomName(newValue.toString(), this.instance); const jitsiUrl = allProps.get(GameMapProperties.JITSI_URL) as string | undefined; diff --git a/front/src/Phaser/UserInput/GameSceneUserInputHandler.ts b/front/src/Phaser/UserInput/GameSceneUserInputHandler.ts index 4d9ac8a9..566e8882 100644 --- a/front/src/Phaser/UserInput/GameSceneUserInputHandler.ts +++ b/front/src/Phaser/UserInput/GameSceneUserInputHandler.ts @@ -53,8 +53,9 @@ export class GameSceneUserInputHandler implements UserInputHandlerInterface { public handlePointerDownEvent(pointer: Phaser.Input.Pointer, gameObjects: Phaser.GameObjects.GameObject[]): void {} public handleSpaceKeyUpEvent(event: Event): Event { - const activatable = this.gameScene.getActivatablesManager().getSelectedActivatableObject(); - if (activatable && activatable.isActivatable()) { + const activatableManager = this.gameScene.getActivatablesManager(); + const activatable = activatableManager.getSelectedActivatableObject(); + if (activatable && activatable.isActivatable() && activatableManager.isSelectingByDistanceEnabled()) { activatable.activate(); } return event; diff --git a/front/src/Stores/OutlineColorStore.ts b/front/src/Stores/OutlineColorStore.ts index a444037d..97a443e1 100644 --- a/front/src/Stores/OutlineColorStore.ts +++ b/front/src/Stores/OutlineColorStore.ts @@ -10,8 +10,6 @@ export function createColorStore() { const updateColor = () => { - console.log('update color'); - console.log(pointedByPointer, pointedByCharacter, followColor, apiColor); set(pointedByPointer ?? pointedByCharacter ?? followColor ?? apiColor); }; From 4bae6e75b1c37913361b5b5f15abfb5f4344b792 Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Mon, 7 Feb 2022 12:37:59 +0100 Subject: [PATCH 07/42] enable activating by distance if in JITSI and JITSI was already opened --- front/src/Phaser/Game/GameScene.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 0f081c14..37b9a066 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -972,6 +972,7 @@ export class GameScene extends DirtyScene { } else { this.startJitsi(roomName, undefined); } + this.activatablesManager.enableSelectingByDistance(); layoutManagerActionStore.removeAction("jitsi"); }; From 81272fbb3cc09736b0577b070020c516d7395fff Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Mon, 7 Feb 2022 13:47:33 +0100 Subject: [PATCH 08/42] yarn.lock update --- yarn.lock | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index b9698f61..adbed748 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,7 @@ # yarn lockfile v1 -"husky@^6.0.0": - "resolved" "https://registry.npmjs.org/husky/-/husky-6.0.0.tgz" - "version" "6.0.0" +husky@^7.0.1: + version "7.0.4" + resolved "https://registry.yarnpkg.com/husky/-/husky-7.0.4.tgz#242048245dc49c8fb1bf0cc7cfb98dd722531535" + integrity sha512-vbaCKN2QLtP/vD4yvs6iz6hBEo6wkSzs8HpRah1Z6aGmF2KW5PdYuAd7uX5a+OyBZHBhd+TFLqgjUgytQr4RvQ== From d4801507289464a2a649a24400df4d3ff80be1fe Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Mon, 7 Feb 2022 14:22:43 +0100 Subject: [PATCH 09/42] cleaner approach to disable activatablesManager distance check if space-event --- front/src/Interfaces/UserInputHandlerInterface.ts | 3 +++ front/src/Phaser/Game/GameScene.ts | 3 --- front/src/Phaser/UserInput/GameSceneUserInputHandler.ts | 9 +++++++++ front/src/Phaser/UserInput/UserInputManager.ts | 4 ++-- front/src/Stores/LayoutManagerStore.ts | 1 + 5 files changed, 15 insertions(+), 5 deletions(-) diff --git a/front/src/Interfaces/UserInputHandlerInterface.ts b/front/src/Interfaces/UserInputHandlerInterface.ts index cf7b2f1c..2a8c6b3e 100644 --- a/front/src/Interfaces/UserInputHandlerInterface.ts +++ b/front/src/Interfaces/UserInputHandlerInterface.ts @@ -9,4 +9,7 @@ export interface UserInputHandlerInterface { handlePointerUpEvent: (pointer: Phaser.Input.Pointer, gameObjects: Phaser.GameObjects.GameObject[]) => void; handlePointerDownEvent: (pointer: Phaser.Input.Pointer, gameObjects: Phaser.GameObjects.GameObject[]) => void; handleSpaceKeyUpEvent: (event: Event) => Event; + + addSpaceEventListener: (callback: Function) => void; + removeSpaceEventListner: (callback: Function) => void; } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 37b9a066..e7242f90 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -957,11 +957,9 @@ export class GameScene extends DirtyScene { this.gameMap.onPropertyChange(GameMapProperties.JITSI_ROOM, (newValue, oldValue, allProps) => { if (newValue === undefined) { - this.activatablesManager.enableSelectingByDistance(); layoutManagerActionStore.removeAction("jitsi"); this.stopJitsi(); } else { - this.activatablesManager.disableSelectingByDistance(); const openJitsiRoomFunction = () => { const roomName = jitsiFactory.getRoomName(newValue.toString(), this.instance); const jitsiUrl = allProps.get(GameMapProperties.JITSI_URL) as string | undefined; @@ -972,7 +970,6 @@ export class GameScene extends DirtyScene { } else { this.startJitsi(roomName, undefined); } - this.activatablesManager.enableSelectingByDistance(); layoutManagerActionStore.removeAction("jitsi"); }; diff --git a/front/src/Phaser/UserInput/GameSceneUserInputHandler.ts b/front/src/Phaser/UserInput/GameSceneUserInputHandler.ts index 566e8882..fc9e83cf 100644 --- a/front/src/Phaser/UserInput/GameSceneUserInputHandler.ts +++ b/front/src/Phaser/UserInput/GameSceneUserInputHandler.ts @@ -60,4 +60,13 @@ export class GameSceneUserInputHandler implements UserInputHandlerInterface { } return event; } + + public addSpaceEventListener(callback: Function): void { + this.gameScene.input.keyboard.addListener("keyup-SPACE", callback); + this.gameScene.getActivatablesManager().disableSelectingByDistance(); + } + public removeSpaceEventListner(callback: Function): void { + this.gameScene.input.keyboard.removeListener("keyup-SPACE", callback); + this.gameScene.getActivatablesManager().enableSelectingByDistance(); + } } diff --git a/front/src/Phaser/UserInput/UserInputManager.ts b/front/src/Phaser/UserInput/UserInputManager.ts index 112ab0e5..e7f814b9 100644 --- a/front/src/Phaser/UserInput/UserInputManager.ts +++ b/front/src/Phaser/UserInput/UserInputManager.ts @@ -223,10 +223,10 @@ export class UserInputManager { } addSpaceEventListner(callback: Function) { - this.scene.input.keyboard.addListener("keyup-SPACE", callback); + this.userInputHandler.addSpaceEventListener(callback); } removeSpaceEventListner(callback: Function) { - this.scene.input.keyboard.removeListener("keyup-SPACE", callback); + this.userInputHandler.removeSpaceEventListner(callback); } destroy(): void { diff --git a/front/src/Stores/LayoutManagerStore.ts b/front/src/Stores/LayoutManagerStore.ts index b6f428aa..e0f8d955 100644 --- a/front/src/Stores/LayoutManagerStore.ts +++ b/front/src/Stores/LayoutManagerStore.ts @@ -1,4 +1,5 @@ import { derived, writable } from "svelte/store"; +import type { ActivatablesManager } from "../Phaser/Game/ActivatablesManager"; import type { UserInputManager } from "../Phaser/UserInput/UserInputManager"; export interface LayoutManagerAction { From c29ce6e9a932de9cdef37406eab0d8278ea1ff69 Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Mon, 7 Feb 2022 14:23:34 +0100 Subject: [PATCH 10/42] prettier --- front/src/Phaser/Entity/Character.ts | 18 +++++++++++++----- front/src/Phaser/Game/ActivatablesManager.ts | 6 ++++-- front/src/Stores/OutlineColorStore.ts | 1 - 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts index 061bc2e5..b411eee7 100644 --- a/front/src/Phaser/Entity/Character.ts +++ b/front/src/Phaser/Entity/Character.ts @@ -163,12 +163,20 @@ export abstract class Character extends Container implements OutlineableInterfac * Returns position based on where player is currently facing * @param shift How far from player should the point of interest be. */ - public getDirectionalActivationPosition(shift: number): { x: number, y: number } { + public getDirectionalActivationPosition(shift: number): { x: number; y: number } { switch (this.lastDirection) { - case PlayerAnimationDirections.Down: { return { x: this.x, y: this.y + shift }; } - case PlayerAnimationDirections.Left: { return { x: this.x - shift, y: this.y }; } - case PlayerAnimationDirections.Right: { return { x: this.x + shift, y: this.y }; } - case PlayerAnimationDirections.Up: { return { x: this.x, y: this.y - shift }; } + case PlayerAnimationDirections.Down: { + return { x: this.x, y: this.y + shift }; + } + case PlayerAnimationDirections.Left: { + return { x: this.x - shift, y: this.y }; + } + case PlayerAnimationDirections.Right: { + return { x: this.x + shift, y: this.y }; + } + case PlayerAnimationDirections.Up: { + return { x: this.x, y: this.y - shift }; + } } } diff --git a/front/src/Phaser/Game/ActivatablesManager.ts b/front/src/Phaser/Game/ActivatablesManager.ts index f136de7c..74c637d7 100644 --- a/front/src/Phaser/Game/ActivatablesManager.ts +++ b/front/src/Phaser/Game/ActivatablesManager.ts @@ -73,7 +73,9 @@ export class ActivatablesManager { } public updateActivatableObjectsDistances(objects: ActivatableInterface[]): void { - const currentPlayerPos = this.currentPlayer.getDirectionalActivationPosition(this.directionalActivationPositionShift); + const currentPlayerPos = this.currentPlayer.getDirectionalActivationPosition( + this.directionalActivationPositionShift + ); for (const object of objects) { const distance = MathUtils.distanceBetween(currentPlayerPos, object.getPosition()); this.activatableObjectsDistances.set(object, distance); @@ -85,7 +87,7 @@ export class ActivatablesManager { object, MathUtils.distanceBetween( this.currentPlayer.getDirectionalActivationPosition(this.directionalActivationPositionShift), - object.getPosition(), + object.getPosition() ) ); } diff --git a/front/src/Stores/OutlineColorStore.ts b/front/src/Stores/OutlineColorStore.ts index 97a443e1..8ecd7293 100644 --- a/front/src/Stores/OutlineColorStore.ts +++ b/front/src/Stores/OutlineColorStore.ts @@ -8,7 +8,6 @@ export function createColorStore() { let pointedByPointer: number | undefined = undefined; let pointedByCharacter: number | undefined = undefined; - const updateColor = () => { set(pointedByPointer ?? pointedByCharacter ?? followColor ?? apiColor); }; From 60c17ecea271732deb78d56d145c4aa8300b49d0 Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Mon, 7 Feb 2022 14:55:51 +0100 Subject: [PATCH 11/42] Re-implement set width of main cowebsite --- front/src/Api/Events/OpenCoWebsiteEvent.ts | 1 + front/src/Api/iframe/nav.ts | 2 + front/src/Phaser/Game/GameMapProperties.ts | 1 + .../Phaser/Game/GameMapPropertiesListener.ts | 31 +++----- front/src/Phaser/Game/GameScene.ts | 1 + front/src/WebRtc/CoWebsiteManager.ts | 73 +++++++++++++------ front/src/WebRtc/JitsiFactory.ts | 10 ++- .../CoWebsite/cowebsite_property_trigger.json | 5 ++ 8 files changed, 81 insertions(+), 43 deletions(-) diff --git a/front/src/Api/Events/OpenCoWebsiteEvent.ts b/front/src/Api/Events/OpenCoWebsiteEvent.ts index 51a17763..b991d3f7 100644 --- a/front/src/Api/Events/OpenCoWebsiteEvent.ts +++ b/front/src/Api/Events/OpenCoWebsiteEvent.ts @@ -5,6 +5,7 @@ export const isOpenCoWebsiteEvent = new tg.IsInterface() url: tg.isString, allowApi: tg.isOptional(tg.isBoolean), allowPolicy: tg.isOptional(tg.isString), + widthPercent: tg.isOptional(tg.isNumber), position: tg.isOptional(tg.isNumber), closable: tg.isOptional(tg.isBoolean), lazy: tg.isOptional(tg.isBoolean), diff --git a/front/src/Api/iframe/nav.ts b/front/src/Api/iframe/nav.ts index d5362b4b..b57f2456 100644 --- a/front/src/Api/iframe/nav.ts +++ b/front/src/Api/iframe/nav.ts @@ -45,6 +45,7 @@ export class WorkadventureNavigationCommands extends IframeApiContribution { + const openCoWebsiteFunction = () => { const coWebsite = coWebsiteManager.addCoWebsite( - url ?? "", + openWebsiteProperty ?? "", this.scene.MapUrlFile, allowApiProperty, websitePolicyProperty, + websiteWidthProperty, websitePositionProperty, false ); @@ -167,13 +166,7 @@ export class GameMapPropertiesListener { uuid: actionId, type: "message", message: websiteTriggerMessageProperty, - callback: () => - openCoWebsiteFunction( - openWebsiteProperty, - allowApiProperty, - websitePolicyProperty, - websitePositionProperty - ), + callback: () => openCoWebsiteFunction(), userInputManager: this.scene.userInputManager, }); } else if (!websiteTriggerProperty || websiteTriggerProperty === ON_ICON_TRIGGER_BUTTON) { @@ -182,6 +175,7 @@ export class GameMapPropertiesListener { this.scene.MapUrlFile, allowApiProperty, websitePolicyProperty, + websiteWidthProperty, websitePositionProperty, false ); @@ -194,12 +188,7 @@ export class GameMapPropertiesListener { } if (!websiteTriggerProperty) { - openCoWebsiteFunction( - openWebsiteProperty, - allowApiProperty, - websitePolicyProperty, - websitePositionProperty - ); + openCoWebsiteFunction(); } }); }; diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 242d97c8..8b62a435 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1265,6 +1265,7 @@ ${escapedMessage} iframeListener.getBaseUrlFromSource(source), openCoWebsite.allowApi, openCoWebsite.allowPolicy, + openCoWebsite.widthPercent, openCoWebsite.position, openCoWebsite.closable ?? true ); diff --git a/front/src/WebRtc/CoWebsiteManager.ts b/front/src/WebRtc/CoWebsiteManager.ts index 9f4f0c36..7b7147b3 100644 --- a/front/src/WebRtc/CoWebsiteManager.ts +++ b/front/src/WebRtc/CoWebsiteManager.ts @@ -43,6 +43,7 @@ export type CoWebsite = { closable: boolean; allowPolicy: string | undefined; allowApi: boolean | undefined; + widthPercent?: number | undefined; jitsi?: boolean; altMessage?: string; }; @@ -82,8 +83,13 @@ class CoWebsiteManager { this.cowebsiteDom.style.width = width + "px"; } - set widthPercent(width: number) { - this.cowebsiteDom.style.width = width + "%"; + get maxWidth(): number { + let maxWidth = 75 * window.innerWidth; + if (maxWidth !== 0) { + maxWidth = Math.round(maxWidth / 100); + } + + return maxWidth; } get height(): number { @@ -94,6 +100,15 @@ class CoWebsiteManager { this.cowebsiteDom.style.height = height + "px"; } + get maxHeight(): number { + let maxHeight = 60 * window.innerHeight; + if (maxHeight !== 0) { + maxHeight = Math.round(maxHeight / 100); + } + + return maxHeight; + } + get verticalMode(): boolean { return window.innerWidth < window.innerHeight; } @@ -191,29 +206,21 @@ class CoWebsiteManager { if (this.verticalMode) { const tempValue = this.height + y; - let maxHeight = 60 * window.innerHeight; - if (maxHeight !== 0) { - maxHeight = Math.round(maxHeight / 100); - } if (tempValue < this.cowebsiteAsideHolderDom.offsetHeight) { this.height = this.cowebsiteAsideHolderDom.offsetHeight; - } else if (tempValue > maxHeight) { - this.height = maxHeight; + } else if (tempValue > this.maxHeight) { + this.height = this.maxHeight; } else { this.height = tempValue; } } else { const tempValue = this.width - x; - let maxWidth = 75 * window.innerWidth; - if (maxWidth !== 0) { - maxWidth = Math.round(maxWidth / 100); - } if (tempValue < this.cowebsiteAsideHolderDom.offsetWidth) { this.width = this.cowebsiteAsideHolderDom.offsetWidth; - } else if (tempValue > maxWidth) { - this.width = maxWidth; + } else if (tempValue > this.maxWidth) { + this.width = this.maxWidth; } else { this.width = tempValue; } @@ -308,7 +315,7 @@ class CoWebsiteManager { this.fire(); } - private loadMain(): void { + private loadMain(openingWidth?: number): void { this.loaderAnimationInterval.interval = setInterval(() => { if (!this.loaderAnimationInterval.trails) { this.loaderAnimationInterval.trails = [0, 1, 2]; @@ -337,6 +344,25 @@ class CoWebsiteManager { trail === 3 ? 0 : trail + 1 ); }, 200); + + if (!this.verticalMode && openingWidth) { + let newWidth = 50; + + if (openingWidth > 100) { + newWidth = 100; + } else if (openingWidth > 1) { + newWidth = openingWidth; + } + + newWidth = Math.round((newWidth * this.maxWidth) / 100); + + if (newWidth < this.cowebsiteAsideHolderDom.offsetWidth) { + newWidth = this.cowebsiteAsideHolderDom.offsetWidth; + } + + this.width = newWidth; + } + this.cowebsiteDom.classList.add("opened"); this.openedMain = iframeStates.loading; } @@ -346,7 +372,6 @@ class CoWebsiteManager { this.resizeAllIframes(); }); this.openedMain = iframeStates.opened; - this.resetStyleMain(); } public resetStyleMain() { @@ -533,6 +558,7 @@ class CoWebsiteManager { base: string, allowApi?: boolean, allowPolicy?: string, + widthPercent?: number, position?: number, closable?: boolean, altMessage?: string @@ -549,6 +575,7 @@ class CoWebsiteManager { closable: closable ?? false, allowPolicy, allowApi, + widthPercent, altMessage, }; @@ -561,14 +588,11 @@ class CoWebsiteManager { iframe: HTMLIFrameElement, allowApi?: boolean, allowPolicy?: string, + widthPercent?: number, position?: number, closable?: boolean, jitsi?: boolean ): CoWebsite { - if (get(coWebsitesNotAsleep).length < 1) { - this.loadMain(); - } - iframe.id = this.generateUniqueId(); const newCoWebsite: CoWebsite = { @@ -578,9 +602,16 @@ class CoWebsiteManager { closable: closable ?? false, allowPolicy, allowApi, + widthPercent, jitsi, }; + if (get(coWebsitesNotAsleep).length < 1) { + coWebsites.remove(newCoWebsite); + coWebsites.add(newCoWebsite, 0); + this.loadMain(widthPercent); + } + if (position === 0) { this.openMain(); setTimeout(() => { @@ -597,7 +628,7 @@ class CoWebsiteManager { if (get(coWebsitesNotAsleep).length < 1) { coWebsites.remove(coWebsite); coWebsites.add(coWebsite, 0); - this.loadMain(); + this.loadMain(coWebsite.widthPercent); } coWebsite.state.set("loading"); diff --git a/front/src/WebRtc/JitsiFactory.ts b/front/src/WebRtc/JitsiFactory.ts index b273a64c..8f7ed952 100644 --- a/front/src/WebRtc/JitsiFactory.ts +++ b/front/src/WebRtc/JitsiFactory.ts @@ -179,7 +179,15 @@ class JitsiFactory { const doResolve = (): void => { const iframe = coWebsiteManager.getCoWebsiteBuffer().querySelector('[id*="jitsi" i]'); if (iframe && this.jitsiApi) { - const coWebsite = coWebsiteManager.addCoWebsiteFromIframe(iframe, false, undefined, 0, false, true); + const coWebsite = coWebsiteManager.addCoWebsiteFromIframe( + iframe, + false, + undefined, + undefined, + 0, + false, + true + ); this.jitsiApi.addListener("videoConferenceLeft", () => { this.closeOrUnload(coWebsite); diff --git a/maps/tests/CoWebsite/cowebsite_property_trigger.json b/maps/tests/CoWebsite/cowebsite_property_trigger.json index 183fad3a..116f399c 100644 --- a/maps/tests/CoWebsite/cowebsite_property_trigger.json +++ b/maps/tests/CoWebsite/cowebsite_property_trigger.json @@ -47,6 +47,11 @@ "name":"openWebsiteTrigger", "type":"string", "value":"onaction" + }, + { + "name":"openWebsiteWidth", + "type":"int", + "value":100 }], "type":"tilelayer", "visible":true, From 75d42209f48d33dc92c955736b5cea4594a4453f Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Mon, 7 Feb 2022 17:09:52 +0100 Subject: [PATCH 12/42] Hide main cowebsite --- .../CoWebsiteThumbnailSlot.svelte | 7 +++- front/src/WebRtc/CoWebsiteManager.ts | 32 ++++++++++++++++++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/front/src/Components/EmbedScreens/CoWebsiteThumbnailSlot.svelte b/front/src/Components/EmbedScreens/CoWebsiteThumbnailSlot.svelte index 809d1985..b71a35c0 100644 --- a/front/src/Components/EmbedScreens/CoWebsiteThumbnailSlot.svelte +++ b/front/src/Components/EmbedScreens/CoWebsiteThumbnailSlot.svelte @@ -5,6 +5,7 @@ import { coWebsitesNotAsleep, mainCoWebsite } from "../../Stores/CoWebsiteStore"; import { highlightedEmbedScreen } from "../../Stores/EmbedScreensStore"; import type { CoWebsite } from "../../WebRtc/CoWebsiteManager"; + import { iframeStates } from "../../WebRtc/CoWebsiteManager"; import { coWebsiteManager } from "../../WebRtc/CoWebsiteManager"; export let index: number; @@ -35,8 +36,12 @@ if ($mainCoWebsite.iframe.id === coWebsite.iframe.id) { const coWebsites = $coWebsitesNotAsleep; const newMain = $highlightedEmbedScreen ?? coWebsites.length > 1 ? coWebsites[1] : undefined; - if (newMain) { + if (newMain && newMain.iframe.id !== $mainCoWebsite.iframe.id) { coWebsiteManager.goToMain(newMain); + } else if (coWebsiteManager.getMainState() === iframeStates.closed) { + coWebsiteManager.displayMain(); + } else { + coWebsiteManager.hideMain(); } } else { highlightedEmbedScreen.toggleHighlight({ diff --git a/front/src/WebRtc/CoWebsiteManager.ts b/front/src/WebRtc/CoWebsiteManager.ts index 7b7147b3..4d6f482c 100644 --- a/front/src/WebRtc/CoWebsiteManager.ts +++ b/front/src/WebRtc/CoWebsiteManager.ts @@ -10,7 +10,7 @@ import { jitsiFactory } from "./JitsiFactory"; import { gameManager } from "../Phaser/Game/GameManager"; import { LayoutMode } from "./LayoutManager"; -enum iframeStates { +export enum iframeStates { closed = 1, loading, // loading an iframe can be slow, so we show some placeholder until it is ready opened, @@ -75,6 +75,10 @@ class CoWebsiteManager { this.resizeAllIframes(); }); + public getMainState() { + return this.openedMain; + } + get width(): number { return this.cowebsiteDom.clientWidth; } @@ -306,6 +310,27 @@ class CoWebsiteManager { }); } + public displayMain() { + const coWebsite = this.getMainCoWebsite(); + if (coWebsite) { + coWebsite.iframe.style.display = "block"; + } + this.loadMain(); + this.openMain(); + this.fire(); + } + + public hideMain() { + const coWebsite = this.getMainCoWebsite(); + if (coWebsite) { + coWebsite.iframe.style.display = "none"; + } + this.cowebsiteDom.classList.add("closing"); + this.cowebsiteDom.classList.remove("opened"); + this.openedMain = iframeStates.closed; + this.fire(); + } + private closeMain(): void { this.toggleFullScreenIcon(true); this.cowebsiteDom.classList.add("closing"); @@ -631,6 +656,11 @@ class CoWebsiteManager { this.loadMain(coWebsite.widthPercent); } + // Check if the main is hide + if (this.getMainCoWebsite() && this.openedMain === iframeStates.closed) { + this.displayMain(); + } + coWebsite.state.set("loading"); const mainCoWebsite = this.getMainCoWebsite(); From c2da4c3906681c8a260380925fdb2660ae1fa376 Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Mon, 7 Feb 2022 17:25:55 +0100 Subject: [PATCH 13/42] Fix active custom menu button --- front/src/Components/Menu/Menu.svelte | 1 + 1 file changed, 1 insertion(+) diff --git a/front/src/Components/Menu/Menu.svelte b/front/src/Components/Menu/Menu.svelte index 69cfa25e..08957a5c 100644 --- a/front/src/Components/Menu/Menu.svelte +++ b/front/src/Components/Menu/Menu.svelte @@ -69,6 +69,7 @@ } else { const customMenu = customMenuIframe.get(menu.label); if (customMenu !== undefined) { + activeSubMenu = menu; props = { url: customMenu.url, allowApi: customMenu.allowApi }; activeComponent = CustomSubMenu; } else { From 433d3a20c6c80b9a00f20a63060558d3c721f485 Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Mon, 7 Feb 2022 17:32:05 +0100 Subject: [PATCH 14/42] Fix raw html not displayed buy terms translation key --- front/src/Components/Login/LoginScene.svelte | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/src/Components/Login/LoginScene.svelte b/front/src/Components/Login/LoginScene.svelte index 4efbc514..0576ac9f 100644 --- a/front/src/Components/Login/LoginScene.svelte +++ b/front/src/Components/Login/LoginScene.svelte @@ -53,7 +53,7 @@
Need for traduction

- {$LL.login.terms()} + {@html $LL.login.terms()}

{/if} From e51300e850f75e869d96d25262a8c2bedfd80715 Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Mon, 7 Feb 2022 17:53:36 +0100 Subject: [PATCH 15/42] Add cowebsite percentWidth property to the documention --- docs/maps/api-nav.md | 27 ++++++++++++------------- docs/maps/images/icon_open_website.png | Bin 0 -> 7971 bytes docs/maps/opening-a-website.md | 7 +++++++ 3 files changed, 20 insertions(+), 14 deletions(-) create mode 100644 docs/maps/images/icon_open_website.png diff --git a/docs/maps/api-nav.md b/docs/maps/api-nav.md index 2743d1ad..8d9fe595 100644 --- a/docs/maps/api-nav.md +++ b/docs/maps/api-nav.md @@ -3,7 +3,7 @@ ### Opening a web page in a new tab -``` +```ts WA.nav.openTab(url: string): void ``` @@ -11,13 +11,13 @@ Opens the webpage at "url" in your browser, in a new tab. Example: -```javascript +```ts WA.nav.openTab('https://www.wikipedia.org/'); ``` ### Opening a web page in the current tab -``` +```ts WA.nav.goToPage(url: string): void ``` @@ -25,14 +25,13 @@ Opens the webpage at "url" in your browser in place of WorkAdventure. WorkAdvent Example: -```javascript +```ts WA.nav.goToPage('https://www.wikipedia.org/'); ``` ### Going to a different map from the script -``` - +```ts WA.nav.goToRoom(url: string): void ``` @@ -43,7 +42,7 @@ global urls: "/_/global/domain/path/map.json[#start-layer-name]" Example: -```javascript +```ts WA.nav.goToRoom("/@/tcm/workadventure/floor0") // workadventure urls WA.nav.goToRoom('../otherMap/map.json'); WA.nav.goToRoom("/_/global/.json#start-layer-2") @@ -51,25 +50,25 @@ WA.nav.goToRoom("/_/global/.json#start-layer-2") ### Opening/closing web page in Co-Websites -``` -WA.nav.openCoWebSite(url: string, allowApi: boolean = false, allowPolicy: string = "", position: number, closable: boolean, lazy: boolean): Promise +```ts +WA.nav.openCoWebSite(url: string, allowApi?: boolean = false, allowPolicy?: string = "", percentWidth?: number, position?: number, closable?: boolean, lazy?: boolean): Promise ``` -Opens the webpage at "url" in an iFrame (on the right side of the screen) or close that iFrame. `allowApi` allows the webpage to use the "IFrame API" and execute script (it is equivalent to putting the `openWebsiteAllowApi` property in the map). `allowPolicy` grants additional access rights to the iFrame. The `allowPolicy` parameter is turned into an [`allow` feature policy in the iFrame](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-allow), position in whitch slot the web page will be open, closable allow to close the webpage also you need to close it by the api and lazy +Opens the webpage at "url" in an iFrame (on the right side of the screen) or close that iFrame. `allowApi` allows the webpage to use the "IFrame API" and execute script (it is equivalent to putting the `openWebsiteAllowApi` property in the map). `allowPolicy` grants additional access rights to the iFrame. The `allowPolicy` parameter is turned into an [`allow` feature policy in the iFrame](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-allow),widthPercent define the width of the main cowebsite beetween the min size and the max size (70% of the viewport), position in whitch slot the web page will be open, closable allow to close the webpage also you need to close it by the api and lazy it's to add the cowebsite but don't load it. Example: -```javascript +```ts const coWebsite = await WA.nav.openCoWebSite('https://www.wikipedia.org/'); -const coWebsiteWorkAdventure = await WA.nav.openCoWebSite('https://workadventu.re/', true, "", 1, true, true); +const coWebsiteWorkAdventure = await WA.nav.openCoWebSite('https://workadventu.re/', true, "", 70, 1, true, true); // ... coWebsite.close(); ``` ### Get all Co-Websites -``` +```ts WA.nav.getCoWebSites(): Promise ``` @@ -77,6 +76,6 @@ Get all opened co-websites with their ids and positions. Example: -```javascript +```ts const coWebsites = await WA.nav.getCowebSites(); ``` diff --git a/docs/maps/images/icon_open_website.png b/docs/maps/images/icon_open_website.png new file mode 100644 index 0000000000000000000000000000000000000000..f3a505bd357feb3b4ea23b124bef0ed9977f6fe4 GIT binary patch literal 7971 zcmbtZX;@R|w%&*p2T;)pDgr82tRSdN%2-iAMZk$chCrnZ1!OP~<^+o(GFz+2T%d@6 z5D*zd2o4MuLYP7bkRSxZ7(#+c2qENt=yT8MbI!SKIhQ{?@a(<5wchovcdc*j2bV3) zq&Dx}3_+08h4bgGLeM%k1c@zg+yMSl8ET#jeyxWaU%0*z{6uX0Ar}0-C(y($@S2}{ zVDR_1-5?KNzgup{;I6mb+=P*(nlG)ZO%J%2qbm4I45iHa5W#gRJ?NLTfZ+m#6`-NlE$q$X$I1I&^4U4BCI- zz%~flv?&yV&Yy1+hrTm5R)C-#J60dP3=brQ5WS%ldvD)H&(0Z{fzA0!O4jOPcK6n5 z>dUF=*6LA={Gqw)*t=31nkOAOJEGoI_p-L{cI^C@qyN%N@tVpK%KYy2=H}+Wp?%xL z>>jVxr^~&acZhO(_mqWgl8p5wZW-9Hua|vWb$#urHG6|ET{1?@Mwfb|_P(asoj+WP zz)XJwmH3-}9ex^C$M6-{7n}35-C# zvT{Pb3cZ8CjQ_mPC+53{+c#va8Kp`s9cRv7mk+AP)NHVKtwSm6pf%(3B{*0W3I*si za9rcjBNMo-VhTx#@UO?@zS6@CUfKEKbkcoD@6pq_) zhUoa$*Ov6shCZ~4N1geY@OOmc4?y*0>W03Y)`EOaw;I|Fxd}>OV&!|+3iKC^(lQH9 zw8h5U>An>lM~^X$(Fy$qA_l0AhyMRW>2FElA0v7ZCRYBqo?%Heclk~-QPLK>{&|Ay z0iSJa&)JzF3f?!~=UEnadG9L{Z~y!GO7f2c+v=nVng7C^1qSn)`;jB{!mNY>G_C1q z=EK!)u!i`6K10=4YbfNd)=K!%lq*veUD8|EE*U9VPCLnyP8V(%={3LUhpVm+Nm?uQ zzTll+TE{OYrF8a;-2`A~u!j%sB-loIomTokTh<@)^oQ=Ig&xG=rnN3VOnd)6uTG_` zG3=4;y31KTuHV1w)KlpHECc16|2tOx$Ikhm-T3_IbMlWhB`|SuUp)geAV6{S>Rz6t zPbTBG&k+Neb4V!Kt>&c|Zelq-d++;Q;I zi^L(^?j}}dB}o-jxAPpsRDEteV#-#ltQDnPI)YrV^`hj`g2CI)$^xUeJxsSQlybRz zDCITx!6O$tl>WZ-(PpWgoyEe?N)9WUvi>DJCtQ7=9kO34-)~ zP8nGrJRD#)9N24dLR0hWoaJ$4+UR}7qU@mOyRNRwnWq8{8geU=n>!MEhk(V0z@iQ{#I=F_7@g4F-M!e8+W2*{;nHMW zB?%UOYdD})URwGJ8offL8H96Hjpjz*-lyG>u)!(nLC{btrXK>sxP_j?ONA=?W7CQ8 z;nB19t6lFC>tqpEe%nyAt36DQU$xEu+sua}Kb$>#_JpCKLVkXJx95wMvM$qnX&Q}o z0z{6dPum(0y{dN0%O|o}EKlu>=9+}{kW09K;_~6AnVAXsX2~t{^Zt(EjH&E*1MhEo|<_!CCxgh1syXCB}q9uc zs)xpA$&~?%^P{H%#?E~VE*FDbW;XII<{NofKfVY@lV`^2F&i|%dos*WLU`vE{(Xh8 zBMM=YxwXsecA6GC)S`iL%P)RvYN~c3JG^rfABj^mOWOCK1}9`lOS9@pST*XWXFAPP zY&Z?ejP%RY4Y~;K4YT{zO2z)S(T(@)?JpQfp0veogrelnw^gbY(w73JU;H8g4R{$N zcSf`ZN0{x?4>=xl|Gulc`(Y%Lhg5&|?Ah2VBG& zliaF^TF$JqFO`wlx-Yh-;p4GYiZBp*hQcYU*0Hgh`wSya>1KJ&Wl>1;5cGIEJLALB z3_97t536NrW=0cNT>MV|Q`gxOdU|s4@$nOQSP;=VVCYs2;FE0Q{Jq6CGs7K1r0Azg zOfWOa!ME<4)wkLnDzS36MIOlxrKbVHR=j%kYHCClf}&m9jgiWX0TS?$>|@j7IH)pntKyZMJ)QivM=4`p@>`@4Vlmz*&|K3b*SoRkcHM{#pGa|U=@~|2!ys} zjv;o`TLRiIaR9eztGw>lt62i*WLbI;+hLZOr=+i4yCyB|4O7qn5u@13s2zaAVOVOM z2VV>tSh>5rc{RWoIp5eBG!EIE2Hpjc!#D}ym1IW^dP#L{?QUoO@3x)c(Z@i*=51&f zQ0Sl8=@d+<8tYg%Ffzlj$(%x9x*3Rkqo$hR8vuZCCe{`Z;?Tiy+b$n%S0)u3@S*9> zE)P(t35kg&`Q3H_?nor^S3--KeoYIgy6ad;aBKc4#rgFDsm9963axt$O(N+JQJFl6 zv4n_Hn|LG+MKzdkDD7_&xrswj@J;+(pTe+qj*bl$5B_I#YDr7rwS)CA)?!eHt0W_Y){oB_$YEjf~9}&5nzh^3JXI|O8uH>>PoaLoBsJR zamBo;r{`*)SJg)mafUO13HEE4byHUfn=L9TIu$hCm(Em#AeV)WeEDZEbnC!?9f%ju zPntFCYzkxlocgZad4-+Vy7<;;`R(GYhfz2+!yP15*BE(!kAgyy3cBHBJ;nEug@wsG z_yJLnGcPD$0k0!#(9)f6MuX=FhTYY6LQz^k8M?fx@__FhLYjL2Ds-3^&$~U(VPVOD z4&l7H#Au0aEn8soKRtc=G&U_QEzV{$R1OkgaooTF8zH|mN5SNiJ2>z504t|7eUv^De^1;vTR%S=F%#-YM9xVfnuVD+}zwP z)CV3B5&D3VsUjVH{e9uw*&>&F5Hzl8q20XTF`9$h=NS+Xz(1MMu8z~6<}9&4YC+I- z^R~)aR;!}XM2_;|!(-HNW@2xlRcpq`GQD;*M(!qWj?#W+8}wb!U40d=nxTN%fr?eN z^~j0Tp5}xNwu1wz<*ZH5oeRWJ7PLdaBi}OX#i8<@8$s^J*mM$Ivxf*}Izqu`VFV(~3r84WNKHB*|{{IA9rK^=ZsI;EI<+jS2N46sWzTU%Q4MwrZ6 z9qu|v^5_9VoNoiGC8SveChh9#8bApmH3K4p%!e$qT50c^gh>2jNhhsqVE>_<)E0e|mA3AOq1sAee!dEDLu6xd(rPVu-Vqg32p5g#z(yc^xf5`|vs%6o;v8sf|im z9X)-uU-;I@5%d=>>GQ`QRg{-+UzJA$#)(xd1|h2H)&ia_cEizrK+~o>;;Y4BcJ5tA zM@OiirvY*~WOFGbjEs2SnsCbw0v=zNxdC#C+Q`4<4&<6nr#}gHKGx0T<@Zj#Q%cs~AuXM3z_kMt z%UGtr5n|RZ^EBN3TE8UJ)Vz~D)X~<~W+GbEWH3#%iVcYnOv?83@78N{#>IuxJBX5^(G)I!p`#N5D;y zO{Z&SWpLIeYe6FY~=6Qo860b8RlfG&YV8Y+P9wR`X0xOO#+s&D<&vykq1 z*sCJvO3==Wt`&Cj2T0VtFRz1~R(cTBaGSXNN>*=gFDn(rPo3yMU?``;K1~*=?}MU_ zDUaJ8*U^zhtxTX4lJ(i=8y1_@lXMzci5UuP(9hEG^iW02FY?xUS(bsiSsyi6gq-di zpjFAJW!gjsS0Dt_W@@b$1QqWG4QYn+c|PLKk_W)aPws;5tjE;la`gM}sl5k{?1Y9! z#U4~ff4i3iU|^m5y!cVAz3s^IO8f>5z4IusnK=0WHQ;B(5J4ZR~!5yM`qqI;o_rzPLv#2>6y|2P8W5?y?EU*NGD+0;qDA z%}h=E8!X06?N!E7pFFwg>#GJ#5csw2z~O3%1E`0f%ggg9!OpW_-r~E0fPFEH>RL{} zgU;+;Uezao(Aw|9X@C!q^og<6rf^!(2u|;CR|gb1wtrl`=gk{qeRiADM8peF^p9;} zduoPnw?8R$EfxG;GsFNmdq%M0orm*wi9zL$?=EjhOHYq`dw-iBc@Mv;qq9?2uoWs# z+{BmaV)9mpp@5}NQ$dWT`iHt%mX|CoZB~1?_JzR|=BpuV_0fHg9zA*!8Z9}rZI`pB6zH7(oKO^bSgx_ibB@`- zo{6W^9kTN3oP|}+YUvK1$G*m6r+3xJ;weEB6cc!PZ-Ep)o+S`*1h&FuZ{%;Y-(5_r zLQ)~3&yjjlC^qFIig?fB5)MP8E zGIQb5rAxk(G^YgE_xJmPHi|b@ZvTf=|E*66#X_RcXb9J(Oy%fp+hx$yO)HF}KiM37 zKO;L!JzY>Ln#Y>JrKSCZB*i1?7*?~bEFO7c(&$Bf>ymB9;p ztX61wby?2?mHS29T#JNo>W75Ll)O;VXU;^Lbq^1X zER~wT?`MZR^PGGC24%H8nSm?toEw^}E%H>0R$Sq4+l7x58HHJQ4-;*FVL*aj(G*G+ zZ@}pw=hb<08S3en1?GsgChvn{M4qSG!LfTJY`Lc|{nMrH;WCyJ2#;GDEQ60+RC1H* z@u~s1$fN?t!IUL~$8tMSmqEJJ-oudT@mj`KScP00vavV7Ljbin`y z^_!>f54E#1u!Jz|EK@~@B-4aT1ii=(?gdlpYW z{EYB5UO1p0@vEn=hUR8bF@G{O>1#YbJ6k>7K{cY&=M(%6NALElS-XV==8`b4?w(DJ zXW5m-AB1e%-}_~+P!?wB;^A@hizFSJ#UQJ!Y?buzr6QUM9N!~;{+=!a(LFp{1mZ8i zJ<=%r9WjLeKu<-UC7Rn2m5rhJ?(&Ifaty{68| zll8gIyn3O!Y6hmvr;O}V=5czW&fWP&vSm&>V)ZN8$u)e)@k*N8iJVUPRV3|ccNq}o-t(0)%{jW-?3kY5Xp$V2baUN zjG*QL7w2x0=empp8o0}vq$3fX!wcQpWblb!8_HfpAu-ycwN>3X42$(r3kIfD6Sy3< nvHJ~4;Fih=_*(!?ip0xds;_ySF(*L7gD#j@o+~qU`RRWE>8~i4 literal 0 HcmV?d00001 diff --git a/docs/maps/opening-a-website.md b/docs/maps/opening-a-website.md index 64b19f1c..a84bde30 100644 --- a/docs/maps/opening-a-website.md +++ b/docs/maps/opening-a-website.md @@ -52,6 +52,13 @@ If you set `openWebsiteTrigger: onaction`, when the user walks on the layer, an If you set `openWebsiteTriggerMessage: your message action` you can edit alert message displayed. If is not defined, the default message displayed is 'Press on SPACE to open the web site'. +If you set `openWebsiteTrigger: onicon`, when the user walks on the layer, an icon will be displayed at the bottom of the screen: + +
+ +
The iFrame will only open if the user clicks on icon
+
+ ### Setting the iFrame "allow" attribute By default, iFrames have limited rights in browsers. For instance, they cannot put their content in fullscreen, they cannot start your webcam, etc... From c77084655899de40fde246b2d5b66d45211b5bea Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Mon, 7 Feb 2022 18:23:02 +0100 Subject: [PATCH 16/42] Fix multi cowebsite creation by trigger --- front/src/Phaser/Game/GameMapPropertiesListener.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/front/src/Phaser/Game/GameMapPropertiesListener.ts b/front/src/Phaser/Game/GameMapPropertiesListener.ts index 32596ddd..497b6cbc 100644 --- a/front/src/Phaser/Game/GameMapPropertiesListener.ts +++ b/front/src/Phaser/Game/GameMapPropertiesListener.ts @@ -169,7 +169,7 @@ export class GameMapPropertiesListener { callback: () => openCoWebsiteFunction(), userInputManager: this.scene.userInputManager, }); - } else if (!websiteTriggerProperty || websiteTriggerProperty === ON_ICON_TRIGGER_BUTTON) { + } else if (websiteTriggerProperty === ON_ICON_TRIGGER_BUTTON) { const coWebsite = coWebsiteManager.addCoWebsite( openWebsiteProperty, this.scene.MapUrlFile, From 7334d59c4fd259e90f18a833b33795c34d8f9813 Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Mon, 7 Feb 2022 18:41:24 +0100 Subject: [PATCH 17/42] Remove priority on iframe add to cowebsites --- front/src/WebRtc/CoWebsiteManager.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/front/src/WebRtc/CoWebsiteManager.ts b/front/src/WebRtc/CoWebsiteManager.ts index 4d6f482c..476526da 100644 --- a/front/src/WebRtc/CoWebsiteManager.ts +++ b/front/src/WebRtc/CoWebsiteManager.ts @@ -618,6 +618,10 @@ class CoWebsiteManager { closable?: boolean, jitsi?: boolean ): CoWebsite { + if (get(coWebsitesNotAsleep).length < 1) { + this.loadMain(widthPercent); + } + iframe.id = this.generateUniqueId(); const newCoWebsite: CoWebsite = { @@ -631,12 +635,6 @@ class CoWebsiteManager { jitsi, }; - if (get(coWebsitesNotAsleep).length < 1) { - coWebsites.remove(newCoWebsite); - coWebsites.add(newCoWebsite, 0); - this.loadMain(widthPercent); - } - if (position === 0) { this.openMain(); setTimeout(() => { From a53cbbff34cfeab2f0e609340eeeb2a8879ba18e Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Tue, 8 Feb 2022 11:04:12 +0100 Subject: [PATCH 18/42] create test map --- front/src/Phaser/Game/GameMap.ts | 1 + maps/tests/index.html | 8 ++++ maps/tests/parallax.json | 80 ++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+) create mode 100644 maps/tests/parallax.json diff --git a/front/src/Phaser/Game/GameMap.ts b/front/src/Phaser/Game/GameMap.ts index 54f91866..31472e83 100644 --- a/front/src/Phaser/Game/GameMap.ts +++ b/front/src/Phaser/Game/GameMap.ts @@ -94,6 +94,7 @@ export class GameMap { depth = DEPTH_OVERLAY_INDEX; } } + console.log(this.phaserLayers); for (const tileset of map.tilesets) { tileset?.tiles?.forEach((tile) => { if (tile.properties) { diff --git a/maps/tests/index.html b/maps/tests/index.html index b4391fee..9d8fe146 100644 --- a/maps/tests/index.html +++ b/maps/tests/index.html @@ -32,6 +32,14 @@ Testing jitsiUrl property + + + Success Failure Pending + + + Test parallax effect + + Success Failure Pending diff --git a/maps/tests/parallax.json b/maps/tests/parallax.json new file mode 100644 index 00000000..01ba9478 --- /dev/null +++ b/maps/tests/parallax.json @@ -0,0 +1,80 @@ +{ "compressionlevel":-1, + "height":50, + "infinite":false, + "layers":[ + { + "data":[23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 12, 12, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 12, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 23, 23, 12, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 12, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 23, 23, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 12, 12, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 12, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 23, 12, 12, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 12, 12, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23], + "height":50, + "id":8, + "name":"background", + "offsetx":-340, + "offsety":-329, + "opacity":1, + "parallaxx":0, + "parallaxy":0, + "type":"tilelayer", + "visible":true, + "width":50, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 1, 1, 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, 1, 0, 0, 1, 1, 0, 0, 1, 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, 1, 1, 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, 1, 1, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 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":50, + "id":1, + "name":"floor", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":50, + "x":0, + "y":0 + }, + { + "data":[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, 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, 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, 24, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 0, 0, 0], + "height":50, + "id":2, + "name":"start", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":50, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":3, + "name":"floorLayer", + "objects":[], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }], + "nextlayerid":9, + "nextobjectid":5, + "orientation":"orthogonal", + "renderorder":"right-down", + "tiledversion":"1.7.2", + "tileheight":32, + "tilesets":[ + { + "columns":11, + "firstgid":1, + "image":"tileset1.png", + "imageheight":352, + "imagewidth":352, + "margin":0, + "name":"tileset1", + "spacing":0, + "tilecount":121, + "tileheight":32, + "tilewidth":32 + }], + "tilewidth":32, + "type":"map", + "version":"1.6", + "width":50 +} \ No newline at end of file From f5f71f32eed44fa2feb13cd55a0341fb5e55dcc5 Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Tue, 8 Feb 2022 11:19:29 +0100 Subject: [PATCH 19/42] Fix cowebsite closing on fast opening/closing --- .../Layouts/PresentationLayout.svelte | 8 +- .../Phaser/Game/GameMapPropertiesListener.ts | 55 ++---- front/src/Phaser/Game/GameScene.ts | 18 +- front/src/WebRtc/CoWebsiteManager.ts | 161 ++++++++---------- front/src/WebRtc/JitsiFactory.ts | 10 +- 5 files changed, 97 insertions(+), 155 deletions(-) diff --git a/front/src/Components/EmbedScreens/Layouts/PresentationLayout.svelte b/front/src/Components/EmbedScreens/Layouts/PresentationLayout.svelte index f4a9f939..1ecb53e0 100644 --- a/front/src/Components/EmbedScreens/Layouts/PresentationLayout.svelte +++ b/front/src/Components/EmbedScreens/Layouts/PresentationLayout.svelte @@ -10,13 +10,9 @@ function closeCoWebsite() { if ($highlightedEmbedScreen?.type === "cowebsite") { if ($highlightedEmbedScreen.embed.closable) { - coWebsiteManager.closeCoWebsite($highlightedEmbedScreen.embed).catch(() => { - console.error("Error during co-website highlighted closing"); - }); + coWebsiteManager.closeCoWebsite($highlightedEmbedScreen.embed); } else { - coWebsiteManager.unloadCoWebsite($highlightedEmbedScreen.embed).catch(() => { - console.error("Error during co-website highlighted unloading"); - }); + coWebsiteManager.unloadCoWebsite($highlightedEmbedScreen.embed); } } } diff --git a/front/src/Phaser/Game/GameMapPropertiesListener.ts b/front/src/Phaser/Game/GameMapPropertiesListener.ts index 497b6cbc..6ac00665 100644 --- a/front/src/Phaser/Game/GameMapPropertiesListener.ts +++ b/front/src/Phaser/Game/GameMapPropertiesListener.ts @@ -9,17 +9,12 @@ import { get } from "svelte/store"; import { ON_ACTION_TRIGGER_BUTTON, ON_ICON_TRIGGER_BUTTON } from "../../WebRtc/LayoutManager"; import type { ITiledMapLayer } from "../Map/ITiledMap"; import { GameMapProperties } from "./GameMapProperties"; - -enum OpenCoWebsiteState { - ASLEEP, - OPENED, - MUST_BE_CLOSE, -} +import type CancelablePromise from "cancelable-promise"; interface OpenCoWebsite { actionId: string; coWebsite?: CoWebsite; - state: OpenCoWebsiteState; + loadPromise?: CancelablePromise; } export class GameMapPropertiesListener { @@ -106,34 +101,16 @@ export class GameMapPropertiesListener { return; } - this.coWebsitesOpenByLayer.set(layer, { + const coWebsiteOpen: OpenCoWebsite = { actionId: actionId, - coWebsite: undefined, - state: OpenCoWebsiteState.ASLEEP, - }); + }; + + this.coWebsitesOpenByLayer.set(layer, coWebsiteOpen); const loadCoWebsiteFunction = (coWebsite: CoWebsite) => { - coWebsiteManager - .loadCoWebsite(coWebsite) - .then((coWebsite) => { - const coWebsiteOpen = this.coWebsitesOpenByLayer.get(layer); - if (coWebsiteOpen && coWebsiteOpen.state === OpenCoWebsiteState.MUST_BE_CLOSE) { - coWebsiteManager.closeCoWebsite(coWebsite).catch(() => { - console.error("Error during a co-website closing"); - }); - this.coWebsitesOpenByLayer.delete(layer); - this.coWebsitesActionTriggerByLayer.delete(layer); - } else { - this.coWebsitesOpenByLayer.set(layer, { - actionId, - coWebsite, - state: OpenCoWebsiteState.OPENED, - }); - } - }) - .catch(() => { - console.error("Error during loading a co-website: " + coWebsite.url); - }); + coWebsiteOpen.loadPromise = coWebsiteManager.loadCoWebsite(coWebsite).catch(() => { + console.error("Error during loading a co-website: " + coWebsite.url); + }); layoutManagerActionStore.removeAction(actionId); }; @@ -149,6 +126,8 @@ export class GameMapPropertiesListener { false ); + coWebsiteOpen.coWebsite = coWebsite; + loadCoWebsiteFunction(coWebsite); }; @@ -180,11 +159,7 @@ export class GameMapPropertiesListener { false ); - const ObjectByLayer = this.coWebsitesOpenByLayer.get(layer); - - if (ObjectByLayer) { - ObjectByLayer.coWebsite = coWebsite; - } + coWebsiteOpen.coWebsite = coWebsite; } if (!websiteTriggerProperty) { @@ -228,12 +203,12 @@ export class GameMapPropertiesListener { return; } - if (coWebsiteOpen.state === OpenCoWebsiteState.ASLEEP) { - coWebsiteOpen.state = OpenCoWebsiteState.MUST_BE_CLOSE; + if (coWebsiteOpen.loadPromise !== undefined) { + coWebsiteOpen.loadPromise.cancel(); } if (coWebsiteOpen.coWebsite !== undefined) { - coWebsiteManager.closeCoWebsite(coWebsiteOpen.coWebsite).catch((e) => console.error(e)); + coWebsiteManager.closeCoWebsite(coWebsiteOpen.coWebsite); } this.coWebsitesOpenByLayer.delete(layer); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 9b758a45..39b84b76 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1289,22 +1289,18 @@ ${escapedMessage} }); }); - iframeListener.registerAnswerer("closeCoWebsite", async (coWebsiteId) => { + iframeListener.registerAnswerer("closeCoWebsite", (coWebsiteId) => { const coWebsite = coWebsiteManager.getCoWebsiteById(coWebsiteId); if (!coWebsite) { throw new Error("Unknown co-website"); } - return coWebsiteManager.closeCoWebsite(coWebsite).catch((error) => { - throw new Error("Error on closing co-website"); - }); + return coWebsiteManager.closeCoWebsite(coWebsite); }); - iframeListener.registerAnswerer("closeCoWebsites", async () => { - return await coWebsiteManager.closeCoWebsites().catch((error) => { - throw new Error("Error on closing all co-websites"); - }); + iframeListener.registerAnswerer("closeCoWebsites", () => { + return coWebsiteManager.closeCoWebsites(); }); iframeListener.registerAnswerer("getMapData", () => { @@ -1568,7 +1564,7 @@ ${escapedMessage} public cleanupClosingScene(): void { // stop playing audio, close any open website, stop any open Jitsi - coWebsiteManager.closeCoWebsites().catch((e) => console.error(e)); + coWebsiteManager.closeCoWebsites(); // Stop the script, if any const scripts = this.getScriptUrls(this.mapFile); for (const script of scripts) { @@ -2138,9 +2134,7 @@ ${escapedMessage} public stopJitsi(): void { const coWebsite = coWebsiteManager.searchJitsi(); if (coWebsite) { - coWebsiteManager.closeCoWebsite(coWebsite).catch((e) => { - console.error("Error during Jitsi co-website closing", e); - }); + coWebsiteManager.closeCoWebsite(coWebsite); } } diff --git a/front/src/WebRtc/CoWebsiteManager.ts b/front/src/WebRtc/CoWebsiteManager.ts index 476526da..0758409f 100644 --- a/front/src/WebRtc/CoWebsiteManager.ts +++ b/front/src/WebRtc/CoWebsiteManager.ts @@ -9,6 +9,7 @@ import { isMediaBreakpointDown } from "../Utils/BreakpointsUtils"; import { jitsiFactory } from "./JitsiFactory"; import { gameManager } from "../Phaser/Game/GameManager"; import { LayoutMode } from "./LayoutManager"; +import CancelablePromise from "cancelable-promise"; export enum iframeStates { closed = 1, @@ -53,11 +54,7 @@ class CoWebsiteManager { private _onResize: Subject = new Subject(); public onResize = this._onResize.asObservable(); - /** - * Quickly going in and out of an iframe trigger can create conflicts between the iframe states. - * So we use this promise to queue up every cowebsite state transition - */ - private currentOperationPromise: Promise = Promise.resolve(); + private cowebsiteDom: HTMLDivElement; private resizing: boolean = false; private gameOverlayDom: HTMLDivElement; @@ -148,13 +145,9 @@ class CoWebsiteManager { } if (coWebsite.closable) { - this.closeCoWebsite(coWebsite).catch(() => { - console.error("Error during closing a co-website by a button"); - }); + this.closeCoWebsite(coWebsite); } else { - this.unloadCoWebsite(coWebsite).catch(() => { - console.error("Error during unloading a co-website by a button"); - }); + this.unloadCoWebsite(coWebsite); } }); @@ -647,7 +640,7 @@ class CoWebsiteManager { return newCoWebsite; } - public loadCoWebsite(coWebsite: CoWebsite): Promise { + public loadCoWebsite(coWebsite: CoWebsite): CancelablePromise { if (get(coWebsitesNotAsleep).length < 1) { coWebsites.remove(coWebsite); coWebsites.add(coWebsite, 0); @@ -663,7 +656,7 @@ class CoWebsiteManager { const mainCoWebsite = this.getMainCoWebsite(); - return new Promise((resolve, reject) => { + return new CancelablePromise((resolve, reject, cancel) => { const onloadPromise = new Promise((resolve) => { coWebsite.iframe.onload = () => { coWebsite.state.set("ready"); @@ -683,8 +676,7 @@ class CoWebsiteManager { jitsiFactory.restart(); } - this.currentOperationPromise = this.currentOperationPromise - .then(() => Promise.race([onloadPromise, onTimeoutPromise])) + const race = CancelablePromise.race([onloadPromise, onTimeoutPromise]) .then(() => { if (mainCoWebsite && mainCoWebsite.iframe.id === coWebsite.iframe.id) { this.openMain(); @@ -701,83 +693,72 @@ class CoWebsiteManager { this.removeCoWebsiteFromStack(coWebsite); return reject(); }); - }); - } - public unloadCoWebsite(coWebsite: CoWebsite): Promise { - return new Promise((resolve, reject) => { - this.removeHighlightCoWebsite(coWebsite); - - coWebsite.iframe.parentNode?.removeChild(coWebsite.iframe); - coWebsite.state.set("asleep"); - coWebsites.remove(coWebsite); - - if (coWebsite.jitsi) { - jitsiFactory.stop(); - const gameScene = gameManager.getCurrentGameScene(); - gameScene.enableMediaBehaviors(); - } - - const mainCoWebsite = this.getMainCoWebsite(); - - if (mainCoWebsite) { - this.removeHighlightCoWebsite(mainCoWebsite); - this.goToMain(mainCoWebsite); - this.resizeAllIframes(); - } else { - this.closeMain(); - } - - coWebsites.add(coWebsite, get(coWebsites).length); - - resolve(); - }); - } - - public closeCoWebsite(coWebsite: CoWebsite): Promise { - this.currentOperationPromise = this.currentOperationPromise.then( - () => - new Promise((resolve) => { - if (coWebsite.jitsi) { - jitsiFactory.destroy(); - const gameScene = gameManager.getCurrentGameScene(); - gameScene.enableMediaBehaviors(); - } - - if (get(coWebsites).length === 1) { - this.fire(); - } - - if (coWebsite.allowApi) { - iframeListener.unregisterIframe(coWebsite.iframe); - } - - this.removeCoWebsiteFromStack(coWebsite); - - const mainCoWebsite = this.getMainCoWebsite(); - - if (mainCoWebsite) { - this.removeHighlightCoWebsite(mainCoWebsite); - this.goToMain(mainCoWebsite); - this.resizeAllIframes(); - } else { - this.closeMain(); - } - resolve(); - }) - ); - return this.currentOperationPromise; - } - - public closeCoWebsites(): Promise { - return (this.currentOperationPromise = this.currentOperationPromise.then(() => { - get(coWebsites).forEach((coWebsite: CoWebsite) => { - this.closeCoWebsite(coWebsite).catch(() => { - console.error("Error during closing a co-website"); - }); + cancel(() => { + race.cancel(); + this.unloadCoWebsite(coWebsite); }); - })); - return this.currentOperationPromise; + }); + } + + public unloadCoWebsite(coWebsite: CoWebsite): void { + this.removeHighlightCoWebsite(coWebsite); + + coWebsite.iframe.parentNode?.removeChild(coWebsite.iframe); + coWebsite.state.set("asleep"); + coWebsites.remove(coWebsite); + + if (coWebsite.jitsi) { + jitsiFactory.stop(); + const gameScene = gameManager.getCurrentGameScene(); + gameScene.enableMediaBehaviors(); + } + + const mainCoWebsite = this.getMainCoWebsite(); + + if (mainCoWebsite) { + this.removeHighlightCoWebsite(mainCoWebsite); + this.goToMain(mainCoWebsite); + this.resizeAllIframes(); + } else { + this.closeMain(); + } + + coWebsites.add(coWebsite, get(coWebsites).length); + } + + public closeCoWebsite(coWebsite: CoWebsite): void { + if (coWebsite.jitsi) { + jitsiFactory.destroy(); + const gameScene = gameManager.getCurrentGameScene(); + gameScene.enableMediaBehaviors(); + } + + if (get(coWebsites).length === 1) { + this.fire(); + } + + if (coWebsite.allowApi) { + iframeListener.unregisterIframe(coWebsite.iframe); + } + + this.removeCoWebsiteFromStack(coWebsite); + + const mainCoWebsite = this.getMainCoWebsite(); + + if (mainCoWebsite) { + this.removeHighlightCoWebsite(mainCoWebsite); + this.goToMain(mainCoWebsite); + this.resizeAllIframes(); + } else { + this.closeMain(); + } + } + + public closeCoWebsites(): void { + get(coWebsites).forEach((coWebsite: CoWebsite) => { + this.closeCoWebsite(coWebsite); + }); } public getGameSize(): { width: number; height: number } { diff --git a/front/src/WebRtc/JitsiFactory.ts b/front/src/WebRtc/JitsiFactory.ts index 8f7ed952..a8f10e10 100644 --- a/front/src/WebRtc/JitsiFactory.ts +++ b/front/src/WebRtc/JitsiFactory.ts @@ -145,7 +145,7 @@ class JitsiFactory { const coWebsite = coWebsiteManager.searchJitsi(); if (coWebsite) { - await coWebsiteManager.closeCoWebsite(coWebsite); + coWebsiteManager.closeCoWebsite(coWebsite); } // Jitsi meet external API maintains some data in local storage @@ -214,13 +214,9 @@ class JitsiFactory { private closeOrUnload = function (coWebsite: CoWebsite) { if (coWebsite.closable) { - coWebsiteManager.closeCoWebsite(coWebsite).catch(() => { - console.error("Error during closing a Jitsi Meet"); - }); + coWebsiteManager.closeCoWebsite(coWebsite); } else { - coWebsiteManager.unloadCoWebsite(coWebsite).catch(() => { - console.error("Error during unloading a Jitsi Meet"); - }); + coWebsiteManager.unloadCoWebsite(coWebsite); } }; From 57bd8783e05ed6bdcef2fb0aabef4beb1728cc6a Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Tue, 8 Feb 2022 13:17:35 +0100 Subject: [PATCH 20/42] scroll factor applied automatically --- front/src/Phaser/Game/GameMap.ts | 2 +- front/src/Phaser/Map/ITiledMap.ts | 2 + maps/tests/parallax.json | 82 ++++++++++++++++++++++++++++--- 3 files changed, 79 insertions(+), 7 deletions(-) diff --git a/front/src/Phaser/Game/GameMap.ts b/front/src/Phaser/Game/GameMap.ts index 31472e83..7e9a486d 100644 --- a/front/src/Phaser/Game/GameMap.ts +++ b/front/src/Phaser/Game/GameMap.ts @@ -85,6 +85,7 @@ export class GameMap { phaserMap .createLayer(layer.name, terrains, (layer.x || 0) * 32, (layer.y || 0) * 32) .setDepth(depth) + .setScrollFactor(layer.parallaxx ?? 1, layer.parallaxy ?? 1) .setAlpha(layer.opacity) .setVisible(layer.visible) .setSize(layer.width, layer.height) @@ -94,7 +95,6 @@ export class GameMap { depth = DEPTH_OVERLAY_INDEX; } } - console.log(this.phaserLayers); for (const tileset of map.tilesets) { tileset?.tiles?.forEach((tile) => { if (tile.properties) { diff --git a/front/src/Phaser/Map/ITiledMap.ts b/front/src/Phaser/Map/ITiledMap.ts index 74810df4..22a8ef86 100644 --- a/front/src/Phaser/Map/ITiledMap.ts +++ b/front/src/Phaser/Map/ITiledMap.ts @@ -78,6 +78,8 @@ export interface ITiledMapTileLayer { width: number; x: number; y: number; + parallaxx: number; + parallaxy: number; /** * Draw order (topdown (default), index) diff --git a/maps/tests/parallax.json b/maps/tests/parallax.json index 01ba9478..ce578090 100644 --- a/maps/tests/parallax.json +++ b/maps/tests/parallax.json @@ -6,9 +6,7 @@ "data":[23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 12, 12, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 12, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 23, 23, 12, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 12, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 23, 23, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 12, 12, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 12, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 12, 12, 12, 12, 23, 23, 23, 12, 12, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 12, 12, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 12, 12, 12, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 12, 12, 12, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23], "height":50, "id":8, - "name":"background", - "offsetx":-340, - "offsety":-329, + "name":"background_bottom", "opacity":1, "parallaxx":0, "parallaxy":0, @@ -18,6 +16,48 @@ "x":0, "y":0 }, + { + "data":[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, 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, 1, 1, 1, 1, 1, 1, 1, 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, 1, 1, 1, 1, 1, 1, 1, 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, 1, 1, 1, 1, 1, 1, 1, 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, 1, 1, 1, 1, 1, 1, 1, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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":50, + "id":9, + "name":"background_middle", + "opacity":1, + "parallaxx":0.75, + "parallaxy":0.75, + "type":"tilelayer", + "visible":true, + "width":50, + "x":0, + "y":0 + }, + { + "data":[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, 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, 12, 12, 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, 12, 12, 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, 12, 12, 12, 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, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 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, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 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, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 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, 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, 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, 12, 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, 12, 12, 12, 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, 12, 12, 12, 12, 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, 12, 12, 12, 12, 12, 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, 12, 12, 12, 12, 12, 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, 12, 12, 12, 12, 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, 12, 12, 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, 12, 12, 12, 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, 12, 12, 12, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":50, + "id":12, + "name":"background_top", + "opacity":1, + "parallaxx":0.5, + "parallaxy":0.5, + "type":"tilelayer", + "visible":true, + "width":50, + "x":0, + "y":0 + }, + { + "data":[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, 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, 0, 0, 0, 36, 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, 92, 0, 25, 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, 103, 0, 0, 0, 82, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":50, + "id":10, + "name":"background_middle_items", + "opacity":1, + "parallaxx":0.75, + "parallaxy":0.75, + "type":"tilelayer", + "visible":true, + "width":50, + "x":0, + "y":0 + }, { "data":[0, 0, 0, 0, 1, 1, 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, 1, 0, 0, 1, 1, 0, 0, 1, 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, 1, 1, 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, 1, 1, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 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, 1, 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":50, @@ -46,15 +86,45 @@ "draworder":"topdown", "id":3, "name":"floorLayer", - "objects":[], + "objects":[ + { + "height":19, + "id":5, + "name":"", + "rotation":0, + "text": + { + "text":"Walk around", + "wrap":true + }, + "type":"", + "visible":true, + "width":93, + "x":62.5, + "y":142.5 + }], "opacity":1, "type":"objectgroup", "visible":true, "x":0, "y":0 + }, + { + "data":[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, 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, 12, 12, 12, 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, 12, 12, 12, 12, 12, 12, 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, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 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, 12, 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, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 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, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 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, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 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, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 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, 12, 12, 12, 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, 12, 12, 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, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 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, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 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, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 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, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 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, 12, 12, 12, 12, 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, 12, 12, 12, 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, 12, 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, 12, 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, 12, 12, 12, 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, 12, 12, 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, 12, 12, 12, 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, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 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, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 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, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 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, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 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, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 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, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 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, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 0, 0, 0, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 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, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 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, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 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, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 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, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 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, 12, 12, 12, 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, 12, 12, 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, 12, 12, 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, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 12, 0, 0, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 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, 12, 12, 0, 0, 0, 0, 0, 0, 0, 0], + "height":50, + "id":13, + "name":"above", + "opacity":1, + "parallaxx":2, + "parallaxy":2, + "type":"tilelayer", + "visible":true, + "width":50, + "x":0, + "y":0 }], - "nextlayerid":9, - "nextobjectid":5, + "nextlayerid":14, + "nextobjectid":7, "orientation":"orthogonal", "renderorder":"right-down", "tiledversion":"1.7.2", From 9cfebecde255fae75ac63f4fba237bb5178f809c Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Tue, 8 Feb 2022 13:32:02 +0100 Subject: [PATCH 21/42] parallaxx and parallaxy made optional --- front/src/Phaser/Map/ITiledMap.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/front/src/Phaser/Map/ITiledMap.ts b/front/src/Phaser/Map/ITiledMap.ts index 22a8ef86..d962648c 100644 --- a/front/src/Phaser/Map/ITiledMap.ts +++ b/front/src/Phaser/Map/ITiledMap.ts @@ -78,8 +78,8 @@ export interface ITiledMapTileLayer { width: number; x: number; y: number; - parallaxx: number; - parallaxy: number; + parallaxx?: number; + parallaxy?: number; /** * Draw order (topdown (default), index) From b32a2970e4f1a911e6a4e6470ad5595a2cc26b61 Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Tue, 8 Feb 2022 15:46:41 +0100 Subject: [PATCH 22/42] collisionGrid now updates on showing / hiding layers --- front/src/Phaser/Game/GameMap.ts | 5 +- front/src/Phaser/Game/GameScene.ts | 5 +- front/src/Utils/PathfindingManager.ts | 4 + maps/tests/DoorTest/map.json | 165 ++++++++++++++++++++++++++ maps/tests/DoorTest/script.php | 26 ++++ maps/tests/index.html | 8 ++ 6 files changed, 210 insertions(+), 3 deletions(-) create mode 100644 maps/tests/DoorTest/map.json create mode 100644 maps/tests/DoorTest/script.php diff --git a/front/src/Phaser/Game/GameMap.ts b/front/src/Phaser/Game/GameMap.ts index 54f91866..98f547d0 100644 --- a/front/src/Phaser/Game/GameMap.ts +++ b/front/src/Phaser/Game/GameMap.ts @@ -120,7 +120,7 @@ export class GameMap { return []; } - public getCollisionsGrid(): number[][] { + public getCollisionGrid(): number[][] { const grid: number[][] = []; for (let y = 0; y < this.map.height; y += 1) { const row: number[] = []; @@ -328,6 +328,9 @@ export class GameMap { private isCollidingAt(x: number, y: number): boolean { for (const layer of this.phaserLayers) { + if (!layer.visible) { + continue; + } if (layer.getTileAt(x, y)?.properties[GameMapProperties.COLLIDES]) { return true; } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 9b758a45..a8406bd2 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -560,7 +560,7 @@ export class GameScene extends DirtyScene { this.pathfindingManager = new PathfindingManager( this, - this.gameMap.getCollisionsGrid(), + this.gameMap.getCollisionGrid(), this.gameMap.getTileDimensions() ); @@ -576,7 +576,7 @@ export class GameScene extends DirtyScene { this.pathfindingManager = new PathfindingManager( this, - this.gameMap.getCollisionsGrid(), + this.gameMap.getCollisionGrid(), this.gameMap.getTileDimensions() ); @@ -1502,6 +1502,7 @@ ${escapedMessage} phaserLayers[i].setCollisionByProperty({ collides: true }, visible); } } + this.pathfindingManager.setCollisionGrid(this.gameMap.getCollisionGrid()); this.markDirty(); } diff --git a/front/src/Utils/PathfindingManager.ts b/front/src/Utils/PathfindingManager.ts index 71205070..c5057ed8 100644 --- a/front/src/Utils/PathfindingManager.ts +++ b/front/src/Utils/PathfindingManager.ts @@ -19,6 +19,10 @@ export class PathfindingManager { this.setEasyStarGrid(collisionsGrid); } + public setCollisionGrid(collisionGrid: number[][]): void { + this.setEasyStarGrid(collisionGrid); + } + public async findPath( start: { x: number; y: number }, end: { x: number; y: number }, diff --git a/maps/tests/DoorTest/map.json b/maps/tests/DoorTest/map.json new file mode 100644 index 00000000..a98f1fdb --- /dev/null +++ b/maps/tests/DoorTest/map.json @@ -0,0 +1,165 @@ +{ "compressionlevel":-1, + "height":30, + "infinite":false, + "layers":[ + { + "data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 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, 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, 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, 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, 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, 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, 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":30, + "id":1, + "name":"floor", + "opacity":1, + "properties":[ + { + "name":"openWebsite", + "type":"string", + "value":"script.php" + }, + { + "name":"openWebsiteAllowApi", + "type":"bool", + "value":true + }], + "type":"tilelayer", + "visible":true, + "width":30, + "x":0, + "y":0 + }, + { + "data":[17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 18, 6, 6, 6, 6, 6, 0, 0, 6, 6, 6, 6, 6, 6, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17, 17, 17, 17, 17, 17, 17, 0, 0, 17, 17, 17, 17, 17, 17, 17, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 28, 28, 28, 28, 28, 28, 28, 0, 0, 28, 28, 28, 28, 28, 28, 28, 29, 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, 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, 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, 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, 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, 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, 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, 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":30, + "id":6, + "name":"furnitures", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":30, + "x":0, + "y":0 + }, + { + "data":[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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 73, 74, 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, 73, 74, 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, 73, 74, 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, 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, 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, 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, 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, 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, 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, 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":30, + "id":8, + "name":"closedPath", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":30, + "x":0, + "y":0 + }, + { + "data":[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, 74, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":30, + "id":2, + "name":"start", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":30, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":3, + "name":"floorLayer", + "objects":[], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }], + "nextlayerid":9, + "nextobjectid":11, + "orientation":"orthogonal", + "properties":[ + { + "name":"openWebsite", + "type":"string", + "value":"script.php" + }, + { + "name":"openWebsiteAllowApi", + "type":"bool", + "value":true + }], + "renderorder":"right-down", + "tiledversion":"1.7.2", + "tileheight":32, + "tilesets":[ + { + "columns":11, + "firstgid":1, + "image":"..\/tileset1.png", + "imageheight":352, + "imagewidth":352, + "margin":0, + "name":"tileset1", + "spacing":0, + "tilecount":121, + "tileheight":32, + "tiles":[ + { + "id":16, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":17, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":27, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":28, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":72, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":73, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }], + "tilewidth":32 + }], + "tilewidth":32, + "type":"map", + "version":"1.6", + "width":30 +} \ No newline at end of file diff --git a/maps/tests/DoorTest/script.php b/maps/tests/DoorTest/script.php new file mode 100644 index 00000000..73f81836 --- /dev/null +++ b/maps/tests/DoorTest/script.php @@ -0,0 +1,26 @@ + + + + + + + + + + + + diff --git a/maps/tests/index.html b/maps/tests/index.html index b4391fee..394555ba 100644 --- a/maps/tests/index.html +++ b/maps/tests/index.html @@ -40,6 +40,14 @@ Test animated tiles + + + Success Failure Pending + + + Test Doors + + Success Failure Pending From fe570c9117926707d7bbd550cba2fa07bca9e747 Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Wed, 9 Feb 2022 10:17:31 +0100 Subject: [PATCH 23/42] moveTo object position --- front/src/Phaser/Game/GameMap.ts | 4 +++ front/src/Phaser/Game/GameScene.ts | 49 ++++++++++++++++++------------ maps/tests/move_to.json | 33 ++++++++++++++++++-- 3 files changed, 65 insertions(+), 21 deletions(-) diff --git a/front/src/Phaser/Game/GameMap.ts b/front/src/Phaser/Game/GameMap.ts index 7e9a486d..8e377ae7 100644 --- a/front/src/Phaser/Game/GameMap.ts +++ b/front/src/Phaser/Game/GameMap.ts @@ -323,6 +323,10 @@ export class GameMap { throw new Error("No possible position found"); } + public getObjectWithName(name: string): ITiledMapObject | undefined { + return this.tiledObjects.find((object) => object.name === name); + } + private getLayersByKey(key: number): Array { return this.flatLayers.filter((flatLayer) => flatLayer.type === "tilelayer" && flatLayer.data[key] !== 0); } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 9b758a45..44992d85 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -568,6 +568,8 @@ export class GameScene extends DirtyScene { this.createCurrentPlayer(); this.removeAllRemotePlayers(); //cleanup the list of remote players in case the scene was rebooted + this.tryMovePlayerWithMoveToParameter(); + this.cameraManager = new CameraManager( this, { x: this.Map.widthInPixels, y: this.Map.heightInPixels }, @@ -1460,9 +1462,9 @@ ${escapedMessage} }); iframeListener.registerAnswerer("movePlayerTo", async (message) => { - const index = this.getGameMap().getTileIndexAt(message.x, message.y); - const startTile = this.getGameMap().getTileIndexAt(this.CurrentPlayer.x, this.CurrentPlayer.y); - const path = await this.getPathfindingManager().findPath(startTile, index, true, true); + const startTileIndex = this.getGameMap().getTileIndexAt(this.CurrentPlayer.x, this.CurrentPlayer.y); + const destinationTileIndex = this.getGameMap().getTileIndexAt(message.x, message.y); + const path = await this.getPathfindingManager().findPath(startTileIndex, destinationTileIndex, true, true); path.shift(); if (path.length === 0) { throw new Error("no path available"); @@ -1626,6 +1628,31 @@ ${escapedMessage} this.MapPlayersByKey.clear(); } + private tryMovePlayerWithMoveToParameter(): void { + const moveToParam = urlManager.getHashParameter("moveTo"); + if (moveToParam) { + try { + const destinationObject = this.gameMap.getObjectWithName(moveToParam); + let endPos; + if (destinationObject) { + endPos = this.gameMap.getTileIndexAt(destinationObject.x, destinationObject.y); + } else { + endPos = this.gameMap.getRandomPositionFromLayer(moveToParam); + } + this.pathfindingManager + .findPath(this.gameMap.getTileIndexAt(this.CurrentPlayer.x, this.CurrentPlayer.y), endPos) + .then((path) => { + if (path && path.length > 0) { + this.CurrentPlayer.setPathToFollow(path).catch((reason) => console.warn(reason)); + } + }) + .catch((reason) => console.warn(reason)); + } catch (err) { + console.warn(`Cannot proceed with moveTo command:\n\t-> ${err}`); + } + } + } + private getExitUrl(layer: ITiledMapLayer): string | undefined { return this.getProperty(layer, GameMapProperties.EXIT_URL) as string | undefined; } @@ -1748,22 +1775,6 @@ ${escapedMessage} this.connection?.emitEmoteEvent(emoteKey); analyticsClient.launchEmote(emoteKey); }); - const moveToParam = urlManager.getHashParameter("moveTo"); - if (moveToParam) { - try { - const endPos = this.gameMap.getRandomPositionFromLayer(moveToParam); - this.pathfindingManager - .findPath(this.gameMap.getTileIndexAt(this.CurrentPlayer.x, this.CurrentPlayer.y), endPos) - .then((path) => { - if (path && path.length > 0) { - this.CurrentPlayer.setPathToFollow(path).catch((reason) => console.warn(reason)); - } - }) - .catch((reason) => console.warn(reason)); - } catch (err) { - console.warn(`Cannot proceed with moveTo command:\n\t-> ${err}`); - } - } } catch (err) { if (err instanceof TextureError) { gameManager.leaveGame(SelectCharacterSceneName, new SelectCharacterScene()); diff --git a/maps/tests/move_to.json b/maps/tests/move_to.json index 796a9d46..6776bd6b 100644 --- a/maps/tests/move_to.json +++ b/maps/tests/move_to.json @@ -3,7 +3,7 @@ "infinite":false, "layers":[ { - "data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + "data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 12, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], "height":10, "id":1, "name":"floor", @@ -204,6 +204,35 @@ "width":92.7120717279925, "x":233.848901257569, "y":135.845612785282 + }, + { + "height":0, + "id":9, + "name":"destination", + "point":true, + "rotation":0, + "type":"", + "visible":true, + "width":0, + "x":207.918025151374, + "y":243.31625523987 + }, + { + "height":45.0829063809967, + "id":10, + "name":"", + "rotation":0, + "text": + { + "halign":"center", + "text":"destination object", + "wrap":true + }, + "type":"", + "visible":true, + "width":83, + "x":167.26, + "y":254.682580344667 }], "opacity":1, "type":"objectgroup", @@ -212,7 +241,7 @@ "y":0 }], "nextlayerid":11, - "nextobjectid":9, + "nextobjectid":11, "orientation":"orthogonal", "renderorder":"right-down", "tiledversion":"1.7.2", From b565080312f02ee51ba22f7f84327a18b130ad77 Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Wed, 9 Feb 2022 10:52:44 +0100 Subject: [PATCH 24/42] parse x and y position from moveTo param --- front/src/Phaser/Game/GameScene.ts | 14 ++++++++++---- front/src/Utils/StringUtils.ts | 12 ++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 front/src/Utils/StringUtils.ts diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 44992d85..8d13cb7b 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -92,6 +92,7 @@ import { MapStore } from "../../Stores/Utils/MapStore"; import { followUsersColorStore } from "../../Stores/FollowStore"; import { GameSceneUserInputHandler } from "../UserInput/GameSceneUserInputHandler"; import { locale } from "../../i18n/i18n-svelte"; +import { StringUtils } from "../../Utils/StringUtils"; export interface GameSceneInitInterface { initPosition: PointInterface | null; reconnecting: boolean; @@ -1632,12 +1633,17 @@ ${escapedMessage} const moveToParam = urlManager.getHashParameter("moveTo"); if (moveToParam) { try { - const destinationObject = this.gameMap.getObjectWithName(moveToParam); let endPos; - if (destinationObject) { - endPos = this.gameMap.getTileIndexAt(destinationObject.x, destinationObject.y); + const posFromParam = StringUtils.parsePointFromParam(moveToParam); + if (posFromParam) { + endPos = this.gameMap.getTileIndexAt(posFromParam.x, posFromParam.y); } else { - endPos = this.gameMap.getRandomPositionFromLayer(moveToParam); + const destinationObject = this.gameMap.getObjectWithName(moveToParam); + if (destinationObject) { + endPos = this.gameMap.getTileIndexAt(destinationObject.x, destinationObject.y); + } else { + endPos = this.gameMap.getRandomPositionFromLayer(moveToParam); + } } this.pathfindingManager .findPath(this.gameMap.getTileIndexAt(this.CurrentPlayer.x, this.CurrentPlayer.y), endPos) diff --git a/front/src/Utils/StringUtils.ts b/front/src/Utils/StringUtils.ts new file mode 100644 index 00000000..1f168c15 --- /dev/null +++ b/front/src/Utils/StringUtils.ts @@ -0,0 +1,12 @@ +export class StringUtils { + public static parsePointFromParam(param: string, separator: string = ","): { x: number; y: number } | undefined { + const values = param.split(separator).map((val) => parseInt(val)); + if (values.length !== 2) { + return; + } + if (isNaN(values[0]) || isNaN(values[1])) { + return; + } + return { x: values[0], y: values[1] }; + } +} From 71e18d1838e8b5dd54589e283cc2213039ab6ddd Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Wed, 9 Feb 2022 11:05:43 +0100 Subject: [PATCH 25/42] update moveTo test map --- maps/tests/move_to.json | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/maps/tests/move_to.json b/maps/tests/move_to.json index 6776bd6b..0801831a 100644 --- a/maps/tests/move_to.json +++ b/maps/tests/move_to.json @@ -233,6 +233,42 @@ "width":83, "x":167.26, "y":254.682580344667 + }, + { + "height":19.6921, + "id":11, + "name":"", + "rotation":0, + "text": + { + "fontfamily":"Sans Serif", + "pixelsize":13, + "text":"...#start&moveTo=destination", + "wrap":true + }, + "type":"", + "visible":true, + "width":202.260327899394, + "x":32.2652715416861, + "y":148.51445302748 + }, + { + "height":19.6921, + "id":12, + "name":"", + "rotation":0, + "text": + { + "fontfamily":"Sans Serif", + "pixelsize":13, + "text":"...#start2&moveTo=200,100", + "wrap":true + }, + "type":"", + "visible":true, + "width":202.26, + "x":32.2654354913834, + "y":169.008165183978 }], "opacity":1, "type":"objectgroup", @@ -241,7 +277,7 @@ "y":0 }], "nextlayerid":11, - "nextobjectid":11, + "nextobjectid":13, "orientation":"orthogonal", "renderorder":"right-down", "tiledversion":"1.7.2", From 9cccdca3dc5d52a1590c55df57f816448d932696 Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Wed, 9 Feb 2022 11:09:20 +0100 Subject: [PATCH 26/42] docs update --- docs/maps/entry-exit.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/maps/entry-exit.md b/docs/maps/entry-exit.md index 2040beb3..25e530de 100644 --- a/docs/maps/entry-exit.md +++ b/docs/maps/entry-exit.md @@ -82,7 +82,11 @@ We are able to direct a Woka to the desired place immediately after spawn. To ma ``` .../my_map.json#moveTo=meeting-room&start ``` +*...or even like this!* +``` +.../my_map.json#start&moveTo=200,100 +``` -For this to work, moveTo must be equal to the layer name of interest. This layer should have at least one tile defined. In case of layer having many tiles, user will go to one of them, randomly selected. +For this to work, moveTo must be equal to the x and y position, layer name, or object name of interest. Layer should have at least one tile defined. In case of layer having many tiles, user will go to one of them, randomly selected. ![](images/moveTo-layer-example.png) \ No newline at end of file From c13672c9dca959b1a260d28fe5dd2d36e7efdc7c Mon Sep 17 00:00:00 2001 From: Hanusiak Piotr Date: Wed, 9 Feb 2022 13:03:14 +0100 Subject: [PATCH 27/42] updated invitation link creator --- front/src/Components/Menu/GuestSubMenu.svelte | 50 +++++++++++++- front/src/Phaser/Game/GameScene.ts | 3 + .../Phaser/Game/StartPositionCalculator.ts | 67 ++++++++++++------- front/src/Stores/StartLayerNamesStore.ts | 6 ++ 4 files changed, 99 insertions(+), 27 deletions(-) create mode 100644 front/src/Stores/StartLayerNamesStore.ts diff --git a/front/src/Components/Menu/GuestSubMenu.svelte b/front/src/Components/Menu/GuestSubMenu.svelte index a0cd94d7..34fba1af 100644 --- a/front/src/Components/Menu/GuestSubMenu.svelte +++ b/front/src/Components/Menu/GuestSubMenu.svelte @@ -1,5 +1,12 @@ @@ -86,7 +85,7 @@ 0}
- {#each [...$coWebsites.values()] as coWebsite, index (coWebsite.iframe.id)} + {#each [...$coWebsites.values()] as coWebsite, index (coWebsite.getId())} {/each}
diff --git a/front/src/Components/EmbedScreens/Layouts/PresentationLayout.svelte b/front/src/Components/EmbedScreens/Layouts/PresentationLayout.svelte index 1ecb53e0..dbf7ee71 100644 --- a/front/src/Components/EmbedScreens/Layouts/PresentationLayout.svelte +++ b/front/src/Components/EmbedScreens/Layouts/PresentationLayout.svelte @@ -9,10 +9,12 @@ function closeCoWebsite() { if ($highlightedEmbedScreen?.type === "cowebsite") { - if ($highlightedEmbedScreen.embed.closable) { + if ($highlightedEmbedScreen.embed.isClosable()) { coWebsiteManager.closeCoWebsite($highlightedEmbedScreen.embed); } else { - coWebsiteManager.unloadCoWebsite($highlightedEmbedScreen.embed); + coWebsiteManager.unloadCoWebsite($highlightedEmbedScreen.embed).catch((err) => { + console.error("Cannot unload co-website", err); + }); } } } @@ -64,9 +66,9 @@ /> {/key} {:else if $highlightedEmbedScreen.type === "cowebsite"} - {#key $highlightedEmbedScreen.embed.iframe.id} + {#key $highlightedEmbedScreen.embed.getId()}
diff --git a/front/src/Phaser/Game/GameMapPropertiesListener.ts b/front/src/Phaser/Game/GameMapPropertiesListener.ts index 6ac00665..22073610 100644 --- a/front/src/Phaser/Game/GameMapPropertiesListener.ts +++ b/front/src/Phaser/Game/GameMapPropertiesListener.ts @@ -1,7 +1,6 @@ import type { GameScene } from "./GameScene"; import type { GameMap } from "./GameMap"; import { scriptUtils } from "../../Api/ScriptUtils"; -import type { CoWebsite } from "../../WebRtc/CoWebsiteManager"; import { coWebsiteManager } from "../../WebRtc/CoWebsiteManager"; import { layoutManagerActionStore } from "../../Stores/LayoutManagerStore"; import { localUserStore } from "../../Connexion/LocalUserStore"; @@ -9,12 +8,12 @@ import { get } from "svelte/store"; import { ON_ACTION_TRIGGER_BUTTON, ON_ICON_TRIGGER_BUTTON } from "../../WebRtc/LayoutManager"; import type { ITiledMapLayer } from "../Map/ITiledMap"; import { GameMapProperties } from "./GameMapProperties"; -import type CancelablePromise from "cancelable-promise"; +import type { CoWebsite } from "../../WebRtc/CoWebsite/CoWesbite"; +import { SimpleCoWebsite } from "../../WebRtc/CoWebsite/SimpleCoWebsite"; interface OpenCoWebsite { actionId: string; coWebsite?: CoWebsite; - loadPromise?: CancelablePromise; } export class GameMapPropertiesListener { @@ -108,26 +107,26 @@ export class GameMapPropertiesListener { this.coWebsitesOpenByLayer.set(layer, coWebsiteOpen); const loadCoWebsiteFunction = (coWebsite: CoWebsite) => { - coWebsiteOpen.loadPromise = coWebsiteManager.loadCoWebsite(coWebsite).catch(() => { - console.error("Error during loading a co-website: " + coWebsite.url); + coWebsiteManager.loadCoWebsite(coWebsite).catch(() => { + console.error("Error during loading a co-website: " + coWebsite.getUrl()); }); layoutManagerActionStore.removeAction(actionId); }; const openCoWebsiteFunction = () => { - const coWebsite = coWebsiteManager.addCoWebsite( - openWebsiteProperty ?? "", - this.scene.MapUrlFile, + const coWebsite = new SimpleCoWebsite( + new URL(openWebsiteProperty ?? "", this.scene.MapUrlFile), allowApiProperty, websitePolicyProperty, websiteWidthProperty, - websitePositionProperty, false ); coWebsiteOpen.coWebsite = coWebsite; + coWebsiteManager.addCoWebsiteToStore(coWebsite, websitePositionProperty); + loadCoWebsiteFunction(coWebsite); }; @@ -149,17 +148,17 @@ export class GameMapPropertiesListener { userInputManager: this.scene.userInputManager, }); } else if (websiteTriggerProperty === ON_ICON_TRIGGER_BUTTON) { - const coWebsite = coWebsiteManager.addCoWebsite( - openWebsiteProperty, - this.scene.MapUrlFile, + const coWebsite = new SimpleCoWebsite( + new URL(openWebsiteProperty ?? "", this.scene.MapUrlFile), allowApiProperty, websitePolicyProperty, websiteWidthProperty, - websitePositionProperty, false ); coWebsiteOpen.coWebsite = coWebsite; + + coWebsiteManager.addCoWebsiteToStore(coWebsite, websitePositionProperty); } if (!websiteTriggerProperty) { @@ -203,12 +202,10 @@ export class GameMapPropertiesListener { return; } - if (coWebsiteOpen.loadPromise !== undefined) { - coWebsiteOpen.loadPromise.cancel(); - } + const coWebsite = coWebsiteOpen.coWebsite; - if (coWebsiteOpen.coWebsite !== undefined) { - coWebsiteManager.closeCoWebsite(coWebsiteOpen.coWebsite); + if (coWebsite) { + coWebsiteManager.closeCoWebsite(coWebsite); } this.coWebsitesOpenByLayer.delete(layer); diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 39b84b76..443a6256 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -5,7 +5,7 @@ import { get, Unsubscriber } from "svelte/store"; import { userMessageManager } from "../../Administration/UserMessageManager"; import { connectionManager } from "../../Connexion/ConnectionManager"; -import { CoWebsite, coWebsiteManager } from "../../WebRtc/CoWebsiteManager"; +import { coWebsiteManager } from "../../WebRtc/CoWebsiteManager"; import { urlManager } from "../../Url/UrlManager"; import { mediaManager } from "../../WebRtc/MediaManager"; import { UserInputManager } from "../UserInput/UserInputManager"; @@ -22,7 +22,13 @@ import { lazyLoadPlayerCharacterTextures, loadCustomTexture } from "../Entity/Pl import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager"; import { ON_ACTION_TRIGGER_BUTTON } from "../../WebRtc/LayoutManager"; import { iframeListener } from "../../Api/IframeListener"; -import { DEBUG_MODE, JITSI_PRIVATE_MODE, MAX_PER_GROUP, POSITION_DELAY } from "../../Enum/EnvironmentVariable"; +import { + DEBUG_MODE, + JITSI_PRIVATE_MODE, + JITSI_URL, + MAX_PER_GROUP, + POSITION_DELAY, +} from "../../Enum/EnvironmentVariable"; import { ProtobufClientUtils } from "../../Network/ProtobufClientUtils"; import { Room } from "../../Connexion/Room"; import { jitsiFactory } from "../../WebRtc/JitsiFactory"; @@ -92,6 +98,9 @@ import { MapStore } from "../../Stores/Utils/MapStore"; import { followUsersColorStore } from "../../Stores/FollowStore"; import { GameSceneUserInputHandler } from "../UserInput/GameSceneUserInputHandler"; import { locale } from "../../i18n/i18n-svelte"; +import { JitsiCoWebsite } from "../../WebRtc/CoWebsite/JitsiCoWebsite"; +import { SimpleCoWebsite } from "../../WebRtc/CoWebsite/SimpleCoWebsite"; +import type { CoWebsite } from "../../WebRtc/CoWebsite/CoWesbite"; export interface GameSceneInitInterface { initPosition: PointInterface | null; reconnecting: boolean; @@ -794,7 +803,19 @@ export class GameScene extends DirtyScene { * Triggered when we receive the JWT token to connect to Jitsi */ this.connection.sendJitsiJwtMessageStream.subscribe((message) => { - this.startJitsi(message.jitsiRoom, message.jwt); + if (!JITSI_URL) { + throw new Error("Missing JITSI_URL environment variable."); + } + + let domain = JITSI_URL; + + if (domain.substring(0, 7) !== "http://" && domain.substring(0, 8) !== "https://") { + domain = `${location.protocol}//${domain}`; + } + + const coWebsite = new JitsiCoWebsite(new URL(domain), false, undefined, undefined, false); + coWebsiteManager.addCoWebsiteToStore(coWebsite, 0); + this.startJitsi(coWebsite, message.jitsiRoom, message.jwt); }); this.messageSubscription = this.connection.worldFullMessageStream.subscribe((message) => { @@ -963,12 +984,25 @@ export class GameScene extends DirtyScene { const openJitsiRoomFunction = () => { const roomName = jitsiFactory.getRoomName(newValue.toString(), this.instance); const jitsiUrl = allProps.get(GameMapProperties.JITSI_URL) as string | undefined; + if (JITSI_PRIVATE_MODE && !jitsiUrl) { const adminTag = allProps.get(GameMapProperties.JITSI_ADMIN_ROOM_TAG) as string | undefined; this.connection?.emitQueryJitsiJwtMessage(roomName, adminTag); } else { - this.startJitsi(roomName, undefined); + let domain = jitsiUrl || JITSI_URL; + if (domain === undefined) { + throw new Error("Missing JITSI_URL environment variable or jitsiUrl parameter in the map."); + } + + if (domain.substring(0, 7) !== "http://" && domain.substring(0, 8) !== "https://") { + domain = `${location.protocol}//${domain}`; + } + + const coWebsite = new JitsiCoWebsite(new URL(domain), false, undefined, undefined, false); + + coWebsiteManager.addCoWebsiteToStore(coWebsite, 0); + this.startJitsi(coWebsite, roomName, undefined); } layoutManagerActionStore.removeAction("jitsi"); }; @@ -1260,13 +1294,11 @@ ${escapedMessage} throw new Error("Unknown query source"); } - const coWebsite = coWebsiteManager.addCoWebsite( - openCoWebsite.url, - iframeListener.getBaseUrlFromSource(source), + const coWebsite: SimpleCoWebsite = new SimpleCoWebsite( + new URL(openCoWebsite.url, iframeListener.getBaseUrlFromSource(source)), openCoWebsite.allowApi, openCoWebsite.allowPolicy, openCoWebsite.widthPercent, - openCoWebsite.position, openCoWebsite.closable ?? true ); @@ -1275,7 +1307,7 @@ ${escapedMessage} } return { - id: coWebsite.iframe.id, + id: coWebsite.getId(), }; }); @@ -1284,7 +1316,7 @@ ${escapedMessage} return coWebsites.map((coWebsite: CoWebsite) => { return { - id: coWebsite.iframe.id, + id: coWebsite.getId(), }; }); }); @@ -2112,7 +2144,7 @@ ${escapedMessage} mediaManager.hideMyCamera(); } - public startJitsi(roomName: string, jwt?: string): void { + public startJitsi(coWebsite: JitsiCoWebsite, roomName: string, jwt?: string): void { const allProps = this.gameMap.getCurrentProperties(); const jitsiConfig = this.safeParseJSONstring( allProps.get(GameMapProperties.JITSI_CONFIG) as string | undefined, @@ -2124,10 +2156,14 @@ ${escapedMessage} ); const jitsiUrl = allProps.get(GameMapProperties.JITSI_URL) as string | undefined; - jitsiFactory.start(roomName, this.playerName, jwt, jitsiConfig, jitsiInterfaceConfig, jitsiUrl).catch(() => { - console.error("Cannot start a Jitsi co-website"); + coWebsite.setJitsiLoadPromise( + jitsiFactory.start(roomName, this.playerName, jwt, jitsiConfig, jitsiInterfaceConfig, jitsiUrl) + ); + + coWebsiteManager.loadCoWebsite(coWebsite).catch((err) => { + console.error(err); }); - this.disableMediaBehaviors(); + analyticsClient.enteredJitsi(roomName, this.room.id); } diff --git a/front/src/Stores/CoWebsiteStore.ts b/front/src/Stores/CoWebsiteStore.ts index 4227c405..7f922db4 100644 --- a/front/src/Stores/CoWebsiteStore.ts +++ b/front/src/Stores/CoWebsiteStore.ts @@ -1,5 +1,6 @@ import { derived, get, writable } from "svelte/store"; -import type { CoWebsite } from "../WebRtc/CoWebsiteManager"; +import type { CoWebsite } from "../WebRtc/CoWebsite/CoWesbite"; +import { JitsiCoWebsite } from "../WebRtc/CoWebsite/JitsiCoWebsite"; function createCoWebsiteStore() { const { subscribe, set, update } = writable(Array()); @@ -9,7 +10,7 @@ function createCoWebsiteStore() { return { subscribe, add: (coWebsite: CoWebsite, position?: number) => { - coWebsite.state.subscribe((value) => { + coWebsite.getStateSubscriber().subscribe((value) => { update((currentArray) => currentArray); }); @@ -31,7 +32,7 @@ function createCoWebsiteStore() { }, remove: (coWebsite: CoWebsite) => { update((currentArray) => [ - ...currentArray.filter((currentCoWebsite) => currentCoWebsite.iframe.id !== coWebsite.iframe.id), + ...currentArray.filter((currentCoWebsite) => currentCoWebsite.getId() !== coWebsite.getId()), ]); }, empty: () => { @@ -43,9 +44,13 @@ function createCoWebsiteStore() { export const coWebsites = createCoWebsiteStore(); export const coWebsitesNotAsleep = derived([coWebsites], ([$coWebsites]) => - $coWebsites.filter((coWebsite) => get(coWebsite.state) !== "asleep") + $coWebsites.filter((coWebsite) => coWebsite.getState() !== "asleep") ); export const mainCoWebsite = derived([coWebsites], ([$coWebsites]) => - $coWebsites.find((coWebsite) => get(coWebsite.state) !== "asleep") + $coWebsites.find((coWebsite) => coWebsite.getState() !== "asleep") +); + +export const jitsiCoWebsite = derived([coWebsites], ([$coWebsites]) => + $coWebsites.find((coWebsite) => coWebsite instanceof JitsiCoWebsite) ); diff --git a/front/src/Stores/EmbedScreensStore.ts b/front/src/Stores/EmbedScreensStore.ts index 0db7c675..724733b3 100644 --- a/front/src/Stores/EmbedScreensStore.ts +++ b/front/src/Stores/EmbedScreensStore.ts @@ -1,5 +1,5 @@ import { derived, get, writable } from "svelte/store"; -import type { CoWebsite } from "../WebRtc/CoWebsiteManager"; +import type { CoWebsite } from "../WebRtc/CoWebsite/CoWesbite"; import { LayoutMode } from "../WebRtc/LayoutManager"; import { coWebsites } from "./CoWebsiteStore"; import { Streamable, streamableCollectionStore } from "./StreamableCollectionStore"; @@ -31,7 +31,7 @@ function createHighlightedEmbedScreenStore() { embedScreen.type !== currentEmbedScreen.type || (embedScreen.type === "cowebsite" && currentEmbedScreen.type === "cowebsite" && - embedScreen.embed.iframe.id !== currentEmbedScreen.embed.iframe.id) || + embedScreen.embed.getId() !== currentEmbedScreen.embed.getId()) || (embedScreen.type === "streamable" && currentEmbedScreen.type === "streamable" && embedScreen.embed.uniqueId !== currentEmbedScreen.embed.uniqueId) diff --git a/front/src/WebRtc/CoWebsite/CoWesbite.ts b/front/src/WebRtc/CoWebsite/CoWesbite.ts new file mode 100644 index 00000000..50ce3b9f --- /dev/null +++ b/front/src/WebRtc/CoWebsite/CoWesbite.ts @@ -0,0 +1,17 @@ +import type CancelablePromise from "cancelable-promise"; +import type { Readable, Writable } from "svelte/store"; + +export type CoWebsiteState = "asleep" | "loading" | "ready"; + +export interface CoWebsite { + getId(): string; + getUrl(): URL; + getState(): CoWebsiteState; + getStateSubscriber(): Readable; + getIframe(): HTMLIFrameElement | undefined; + getLoadIframe(): CancelablePromise | undefined; + getWidthPercent(): number | undefined; + isClosable(): boolean; + load(): CancelablePromise; + unload(): Promise; +} diff --git a/front/src/WebRtc/CoWebsite/JitsiCoWebsite.ts b/front/src/WebRtc/CoWebsite/JitsiCoWebsite.ts new file mode 100644 index 00000000..66491569 --- /dev/null +++ b/front/src/WebRtc/CoWebsite/JitsiCoWebsite.ts @@ -0,0 +1,61 @@ +import CancelablePromise from "cancelable-promise"; +import { gameManager } from "../../Phaser/Game/GameManager"; +import { coWebsiteManager } from "../CoWebsiteManager"; +import { jitsiFactory } from "../JitsiFactory"; +import { SimpleCoWebsite } from "./SimpleCoWebsite"; + +export class JitsiCoWebsite extends SimpleCoWebsite { + private jitsiLoadPromise?: CancelablePromise; + + constructor(url: URL, allowApi?: boolean, allowPolicy?: string, widthPercent?: number, closable?: boolean) { + const coWebsite = coWebsiteManager.searchJitsi(); + + if (coWebsite) { + coWebsiteManager.closeCoWebsite(coWebsite); + } + + super(url, allowApi, allowPolicy, widthPercent, closable); + } + + setJitsiLoadPromise(promise: CancelablePromise): void { + this.jitsiLoadPromise = promise; + } + + load(): CancelablePromise { + return new CancelablePromise((resolve, reject, cancel) => { + this.state.set("loading"); + + gameManager.getCurrentGameScene().disableMediaBehaviors(); + jitsiFactory.restart(); + + if (!this.jitsiLoadPromise) { + return reject("Undefined Jitsi start callback"); + } + + const jitsiLoading = this.jitsiLoadPromise + .then((iframe) => { + this.iframe = iframe; + this.iframe.classList.add("pixel"); + this.state.set("ready"); + return resolve(iframe); + }) + .catch((err) => { + return reject(err); + }); + + cancel(() => { + jitsiLoading.cancel(); + this.unload().catch((err) => { + console.error("Cannot unload Jitsi co-website while cancel loading", err); + }); + }); + }); + } + + unload(): Promise { + jitsiFactory.destroy(); + gameManager.getCurrentGameScene().enableMediaBehaviors(); + + return super.unload(); + } +} diff --git a/front/src/WebRtc/CoWebsite/SimpleCoWebsite.ts b/front/src/WebRtc/CoWebsite/SimpleCoWebsite.ts new file mode 100644 index 00000000..1ad585b4 --- /dev/null +++ b/front/src/WebRtc/CoWebsite/SimpleCoWebsite.ts @@ -0,0 +1,133 @@ +import CancelablePromise from "cancelable-promise"; +import { get, Readable, writable, Writable } from "svelte/store"; +import { iframeListener } from "../../Api/IframeListener"; +import { coWebsiteManager } from "../CoWebsiteManager"; +import type { CoWebsite, CoWebsiteState } from "./CoWesbite"; + +export class SimpleCoWebsite implements CoWebsite { + protected id: string; + protected url: URL; + protected state: Writable; + protected iframe?: HTMLIFrameElement; + protected loadIframe?: CancelablePromise; + protected allowApi?: boolean; + protected allowPolicy?: string; + protected widthPercent?: number; + protected closable: boolean; + + constructor(url: URL, allowApi?: boolean, allowPolicy?: string, widthPercent?: number, closable?: boolean) { + this.id = coWebsiteManager.generateUniqueId(); + this.url = url; + this.state = writable("asleep" as CoWebsiteState); + this.allowApi = allowApi; + this.allowPolicy = allowPolicy; + this.widthPercent = widthPercent; + this.closable = closable ?? false; + } + + getId(): string { + return this.id; + } + + getUrl(): URL { + return this.url; + } + + getState(): CoWebsiteState { + return get(this.state); + } + + getStateSubscriber(): Readable { + return this.state; + } + + getIframe(): HTMLIFrameElement | undefined { + return this.iframe; + } + + getLoadIframe(): CancelablePromise | undefined { + return this.loadIframe; + } + + getWidthPercent(): number | undefined { + return this.widthPercent; + } + + isClosable(): boolean { + return this.closable; + } + + load(): CancelablePromise { + this.loadIframe = new CancelablePromise((resolve, reject, cancel) => { + this.state.set("loading"); + + const iframe = document.createElement("iframe"); + this.iframe = iframe; + this.iframe.src = this.url.toString(); + this.iframe.id = this.id; + + if (this.allowPolicy) { + this.iframe.allow = this.allowPolicy; + } + + if (this.allowApi) { + iframeListener.registerIframe(this.iframe); + } + + this.iframe.classList.add("pixel"); + + const onloadPromise = new Promise((resolve) => { + if (this.iframe) { + this.iframe.onload = () => { + this.state.set("ready"); + resolve(); + }; + } + }); + + const onTimeoutPromise = new Promise((resolve) => { + setTimeout(() => resolve(), 2000); + }); + + coWebsiteManager.getCoWebsiteBuffer().appendChild(this.iframe); + + const race = CancelablePromise.race([onloadPromise, onTimeoutPromise]) + .then(() => { + return resolve(iframe); + }) + .catch((err) => { + console.error("Error on co-website loading => ", err); + return reject(); + }); + + cancel(() => { + race.cancel(); + this.unload().catch((err) => { + console.error("Cannot unload co-website while cancel loading", err); + }); + }); + }); + + return this.loadIframe; + } + + unload(): Promise { + return new Promise((resolve) => { + if (this.iframe) { + if (this.allowApi) { + iframeListener.unregisterIframe(this.iframe); + } + this.iframe.parentNode?.removeChild(this.iframe); + } + + if (this.loadIframe) { + this.loadIframe.cancel(); + this.loadIframe = undefined; + } + + this.state.set("asleep"); + + resolve(); + }); + } +} diff --git a/front/src/WebRtc/CoWebsiteManager.ts b/front/src/WebRtc/CoWebsiteManager.ts index 0758409f..f67b6e18 100644 --- a/front/src/WebRtc/CoWebsiteManager.ts +++ b/front/src/WebRtc/CoWebsiteManager.ts @@ -1,15 +1,13 @@ import { HtmlUtils } from "./HtmlUtils"; import { Subject } from "rxjs"; -import { iframeListener } from "../Api/IframeListener"; import { waScaleManager } from "../Phaser/Services/WaScaleManager"; -import { coWebsites, coWebsitesNotAsleep, mainCoWebsite } from "../Stores/CoWebsiteStore"; -import { get, Writable, writable } from "svelte/store"; +import { coWebsites, coWebsitesNotAsleep, jitsiCoWebsite, mainCoWebsite } from "../Stores/CoWebsiteStore"; +import { get } from "svelte/store"; import { embedScreenLayout, highlightedEmbedScreen } from "../Stores/EmbedScreensStore"; import { isMediaBreakpointDown } from "../Utils/BreakpointsUtils"; -import { jitsiFactory } from "./JitsiFactory"; -import { gameManager } from "../Phaser/Game/GameManager"; import { LayoutMode } from "./LayoutManager"; -import CancelablePromise from "cancelable-promise"; +import type { CoWebsite } from "./CoWebsite/CoWesbite"; +import type CancelablePromise from "cancelable-promise"; export enum iframeStates { closed = 1, @@ -35,20 +33,6 @@ interface TouchMoveCoordinates { y: number; } -export type CoWebsiteState = "asleep" | "loading" | "ready"; - -export type CoWebsite = { - iframe: HTMLIFrameElement; - url: URL; - state: Writable; - closable: boolean; - allowPolicy: string | undefined; - allowApi: boolean | undefined; - widthPercent?: number | undefined; - jitsi?: boolean; - altMessage?: string; -}; - class CoWebsiteManager { private openedMain: iframeStates = iframeStates.closed; @@ -144,10 +128,12 @@ class CoWebsiteManager { throw new Error("Undefined main co-website on closing"); } - if (coWebsite.closable) { + if (coWebsite.isClosable()) { this.closeCoWebsite(coWebsite); } else { - this.unloadCoWebsite(coWebsite); + this.unloadCoWebsite(coWebsite).catch((err) => { + console.error("Cannot unload co-website on click on close button", err); + }); } }); @@ -234,7 +220,10 @@ class CoWebsiteManager { return; } - coWebsite.iframe.style.display = "none"; + const iframe = coWebsite.getIframe(); + if (iframe) { + iframe.style.display = "none"; + } this.resizing = true; document.addEventListener("mousemove", movecallback); }); @@ -250,7 +239,10 @@ class CoWebsiteManager { return; } - coWebsite.iframe.style.display = "flex"; + const iframe = coWebsite.getIframe(); + if (iframe) { + iframe.style.display = "flex"; + } this.resizing = false; }); @@ -263,7 +255,10 @@ class CoWebsiteManager { return; } - coWebsite.iframe.style.display = "none"; + const iframe = coWebsite.getIframe(); + if (iframe) { + iframe.style.display = "none"; + } this.resizing = true; const touchEvent = event.touches[0]; this.previousTouchMoveCoordinates = { x: touchEvent.pageX, y: touchEvent.pageY }; @@ -282,7 +277,10 @@ class CoWebsiteManager { return; } - coWebsite.iframe.style.display = "flex"; + const iframe = coWebsite.getIframe(); + if (iframe) { + iframe.style.display = "flex"; + } this.resizing = false; }); } @@ -306,9 +304,12 @@ class CoWebsiteManager { public displayMain() { const coWebsite = this.getMainCoWebsite(); if (coWebsite) { - coWebsite.iframe.style.display = "block"; + const iframe = coWebsite.getIframe(); + if (iframe) { + iframe.style.display = "block"; + } } - this.loadMain(); + this.loadMain(coWebsite?.getWidthPercent()); this.openMain(); this.fire(); } @@ -316,7 +317,10 @@ class CoWebsiteManager { public hideMain() { const coWebsite = this.getMainCoWebsite(); if (coWebsite) { - coWebsite.iframe.style.display = "none"; + const iframe = coWebsite.getIframe(); + if (iframe) { + iframe.style.display = "none"; + } } this.cowebsiteDom.classList.add("closing"); this.cowebsiteDom.classList.remove("opened"); @@ -402,7 +406,9 @@ class CoWebsiteManager { } public getCoWebsiteById(coWebsiteId: string): CoWebsite | undefined { - return get(coWebsites).find((coWebsite: CoWebsite) => coWebsite.iframe.id === coWebsiteId); + return get(coWebsites).find((coWebsite: CoWebsite) => { + return coWebsite.getId() === coWebsiteId; + }); } private getCoWebsiteByPosition(position: number): CoWebsite | undefined { @@ -422,7 +428,9 @@ class CoWebsiteManager { } private getPositionByCoWebsite(coWebsite: CoWebsite): number { - return get(coWebsites).findIndex((currentCoWebsite) => currentCoWebsite.iframe.id === coWebsite.iframe.id); + return get(coWebsites).findIndex((currentCoWebsite) => { + return currentCoWebsite.getId() === coWebsite.getId(); + }); } private getSlotByCowebsite(coWebsite: CoWebsite): HTMLDivElement | undefined { @@ -436,7 +444,7 @@ class CoWebsiteManager { if (index === 0) { id += "main"; } else { - id += coWebsite.iframe.id; + id += coWebsite.getId(); } const slot = HtmlUtils.getElementById(id); @@ -453,60 +461,72 @@ class CoWebsiteManager { const bounding = coWebsiteSlot.getBoundingClientRect(); - coWebsite.iframe.style.top = bounding.top + "px"; - coWebsite.iframe.style.left = bounding.left + "px"; - coWebsite.iframe.style.width = bounding.right - bounding.left + "px"; - coWebsite.iframe.style.height = bounding.bottom - bounding.top + "px"; + const iframe = coWebsite.getIframe(); + + if (iframe) { + iframe.style.top = bounding.top + "px"; + iframe.style.left = bounding.left + "px"; + iframe.style.width = bounding.right - bounding.left + "px"; + iframe.style.height = bounding.bottom - bounding.top + "px"; + } } public resizeAllIframes() { const mainCoWebsite = this.getCoWebsiteByPosition(0); + const mainIframe = mainCoWebsite?.getIframe(); const highlightEmbed = get(highlightedEmbedScreen); - get(coWebsites).forEach((coWebsite) => { - const notMain = !mainCoWebsite || (mainCoWebsite && mainCoWebsite.iframe.id !== coWebsite.iframe.id); + get(coWebsites).forEach((coWebsite: CoWebsite) => { + const iframe = coWebsite.getIframe(); + if (!iframe) { + return; + } + + const notMain = !mainCoWebsite || (mainCoWebsite && mainIframe && mainIframe.id !== iframe.id); const notHighlighEmbed = !highlightEmbed || (highlightEmbed && (highlightEmbed.type !== "cowebsite" || - (highlightEmbed.type === "cowebsite" && - highlightEmbed.embed.iframe.id !== coWebsite.iframe.id))); + (highlightEmbed.type === "cowebsite" && highlightEmbed.embed.getId() !== coWebsite.getId()))); - if (coWebsite.iframe.classList.contains("main") && notMain) { - coWebsite.iframe.classList.remove("main"); + if (iframe.classList.contains("main") && notMain) { + iframe.classList.remove("main"); } - if (coWebsite.iframe.classList.contains("highlighted") && notHighlighEmbed) { - coWebsite.iframe.classList.remove("highlighted"); - coWebsite.iframe.classList.add("pixel"); - coWebsite.iframe.style.top = "-1px"; - coWebsite.iframe.style.left = "-1px"; + if (iframe.classList.contains("highlighted") && notHighlighEmbed) { + iframe.classList.remove("highlighted"); + iframe.classList.add("pixel"); + iframe.style.top = "-1px"; + iframe.style.left = "-1px"; } if (notMain && notHighlighEmbed) { - coWebsite.iframe.classList.add("pixel"); - coWebsite.iframe.style.top = "-1px"; - coWebsite.iframe.style.left = "-1px"; + iframe.classList.add("pixel"); + iframe.style.top = "-1px"; + iframe.style.left = "-1px"; } this.setIframeOffset(coWebsite); }); - if (mainCoWebsite) { - mainCoWebsite.iframe.classList.add("main"); - mainCoWebsite.iframe.classList.remove("pixel"); + if (mainIframe) { + mainIframe.classList.add("main"); + mainIframe.classList.remove("pixel"); } if (highlightEmbed && highlightEmbed.type === "cowebsite") { - highlightEmbed.embed.iframe.classList.add("highlighted"); - highlightEmbed.embed.iframe.classList.remove("pixel"); + const highlightEmbedIframe = highlightEmbed.embed.getIframe(); + if (highlightEmbedIframe) { + highlightEmbedIframe.classList.add("highlighted"); + highlightEmbedIframe.classList.remove("pixel"); + } } } private removeHighlightCoWebsite(coWebsite: CoWebsite) { const highlighted = get(highlightedEmbedScreen); - if (highlighted && highlighted.type === "cowebsite" && highlighted.embed.iframe.id === coWebsite.iframe.id) { + if (highlighted && highlighted.type === "cowebsite" && highlighted.embed.getId() === coWebsite.getId()) { highlightedEmbedScreen.removeHighlight(); } } @@ -519,7 +539,9 @@ class CoWebsiteManager { this.closeMain(); } - coWebsite.iframe.remove(); + coWebsite.unload().catch((err) => { + console.error("Cannot unload cowebsite on remove from stack"); + }); } public goToMain(coWebsite: CoWebsite) { @@ -531,8 +553,8 @@ class CoWebsiteManager { isMediaBreakpointDown("lg") && get(embedScreenLayout) === LayoutMode.Presentation && mainCoWebsite && - mainCoWebsite.iframe.id !== coWebsite.iframe.id && - get(mainCoWebsite.state) !== "asleep" + mainCoWebsite.getId() !== coWebsite.getId() && + mainCoWebsite.getState() !== "asleep" ) { highlightedEmbedScreen.toggleHighlight({ type: "cowebsite", @@ -544,25 +566,15 @@ class CoWebsiteManager { } public searchJitsi(): CoWebsite | undefined { - return get(coWebsites).find((coWebsite: CoWebsite) => coWebsite.jitsi); + return get(jitsiCoWebsite); } - private initialiseCowebsite(coWebsite: CoWebsite, position: number | undefined) { - if (coWebsite.allowPolicy) { - coWebsite.iframe.allow = coWebsite.allowPolicy; - } - - if (coWebsite.allowApi) { - iframeListener.registerIframe(coWebsite.iframe); - } - - coWebsite.iframe.classList.add("pixel"); - + public addCoWebsiteToStore(coWebsite: CoWebsite, position: number | undefined) { const coWebsitePosition = position === undefined ? get(coWebsites).length : position; coWebsites.add(coWebsite, coWebsitePosition); } - private generateUniqueId() { + public generateUniqueId() { let id = undefined; do { id = "cowebsite-iframe-" + (Math.random() + 1).toString(36).substring(7); @@ -571,80 +583,11 @@ class CoWebsiteManager { return id; } - public addCoWebsite( - url: string, - base: string, - allowApi?: boolean, - allowPolicy?: string, - widthPercent?: number, - position?: number, - closable?: boolean, - altMessage?: string - ): CoWebsite { - const iframe = document.createElement("iframe"); - const fullUrl = new URL(url, base); - iframe.src = fullUrl.toString(); - iframe.id = this.generateUniqueId(); - - const newCoWebsite: CoWebsite = { - iframe, - url: fullUrl, - state: writable("asleep" as CoWebsiteState), - closable: closable ?? false, - allowPolicy, - allowApi, - widthPercent, - altMessage, - }; - - this.initialiseCowebsite(newCoWebsite, position); - - return newCoWebsite; - } - - public addCoWebsiteFromIframe( - iframe: HTMLIFrameElement, - allowApi?: boolean, - allowPolicy?: string, - widthPercent?: number, - position?: number, - closable?: boolean, - jitsi?: boolean - ): CoWebsite { - if (get(coWebsitesNotAsleep).length < 1) { - this.loadMain(widthPercent); - } - - iframe.id = this.generateUniqueId(); - - const newCoWebsite: CoWebsite = { - iframe, - url: new URL(iframe.src), - state: writable("ready" as CoWebsiteState), - closable: closable ?? false, - allowPolicy, - allowApi, - widthPercent, - jitsi, - }; - - if (position === 0) { - this.openMain(); - setTimeout(() => { - this.fire(); - }, animationTime); - } - - this.initialiseCowebsite(newCoWebsite, position); - - return newCoWebsite; - } - - public loadCoWebsite(coWebsite: CoWebsite): CancelablePromise { + public loadCoWebsite(coWebsite: CoWebsite): CancelablePromise { if (get(coWebsitesNotAsleep).length < 1) { coWebsites.remove(coWebsite); coWebsites.add(coWebsite, 0); - this.loadMain(coWebsite.widthPercent); + this.loadMain(coWebsite.getWidthPercent()); } // Check if the main is hide @@ -652,96 +595,56 @@ class CoWebsiteManager { this.displayMain(); } - coWebsite.state.set("loading"); + const coWebsiteLloading = coWebsite + .load() + .then(() => { + const mainCoWebsite = this.getMainCoWebsite(); + if (mainCoWebsite && mainCoWebsite.getId() === coWebsite.getId()) { + this.openMain(); - const mainCoWebsite = this.getMainCoWebsite(); - - return new CancelablePromise((resolve, reject, cancel) => { - const onloadPromise = new Promise((resolve) => { - coWebsite.iframe.onload = () => { - coWebsite.state.set("ready"); - resolve(); - }; + setTimeout(() => { + this.fire(); + }, animationTime); + } + this.resizeAllIframes(); + }) + .catch((err) => { + console.error("Error on co-website loading => ", err); + this.removeCoWebsiteFromStack(coWebsite); }); - const onTimeoutPromise = new Promise((resolve) => { - setTimeout(() => resolve(), 2000); - }); - - this.cowebsiteBufferDom.appendChild(coWebsite.iframe); - - if (coWebsite.jitsi) { - const gameScene = gameManager.getCurrentGameScene(); - gameScene.disableMediaBehaviors(); - jitsiFactory.restart(); - } - - const race = CancelablePromise.race([onloadPromise, onTimeoutPromise]) - .then(() => { - if (mainCoWebsite && mainCoWebsite.iframe.id === coWebsite.iframe.id) { - this.openMain(); - - setTimeout(() => { - this.fire(); - }, animationTime); - } - - return resolve(coWebsite); - }) - .catch((err) => { - console.error("Error on co-website loading => ", err); - this.removeCoWebsiteFromStack(coWebsite); - return reject(); - }); - - cancel(() => { - race.cancel(); - this.unloadCoWebsite(coWebsite); - }); - }); + return coWebsiteLloading; } - public unloadCoWebsite(coWebsite: CoWebsite): void { + public unloadCoWebsite(coWebsite: CoWebsite): Promise { this.removeHighlightCoWebsite(coWebsite); - coWebsite.iframe.parentNode?.removeChild(coWebsite.iframe); - coWebsite.state.set("asleep"); - coWebsites.remove(coWebsite); + return coWebsite + .unload() + .then(() => { + coWebsites.remove(coWebsite); + const mainCoWebsite = this.getMainCoWebsite(); - if (coWebsite.jitsi) { - jitsiFactory.stop(); - const gameScene = gameManager.getCurrentGameScene(); - gameScene.enableMediaBehaviors(); - } + if (mainCoWebsite) { + this.removeHighlightCoWebsite(mainCoWebsite); + this.goToMain(mainCoWebsite); + this.resizeAllIframes(); + } else { + this.closeMain(); + } - const mainCoWebsite = this.getMainCoWebsite(); - - if (mainCoWebsite) { - this.removeHighlightCoWebsite(mainCoWebsite); - this.goToMain(mainCoWebsite); - this.resizeAllIframes(); - } else { - this.closeMain(); - } - - coWebsites.add(coWebsite, get(coWebsites).length); + coWebsites.add(coWebsite, get(coWebsites).length); + }) + .catch(() => { + console.error(); + }); } public closeCoWebsite(coWebsite: CoWebsite): void { - if (coWebsite.jitsi) { - jitsiFactory.destroy(); - const gameScene = gameManager.getCurrentGameScene(); - gameScene.enableMediaBehaviors(); - } - if (get(coWebsites).length === 1) { this.fire(); } - if (coWebsite.allowApi) { - iframeListener.unregisterIframe(coWebsite.iframe); - } - this.removeCoWebsiteFromStack(coWebsite); const mainCoWebsite = this.getMainCoWebsite(); diff --git a/front/src/WebRtc/JitsiFactory.ts b/front/src/WebRtc/JitsiFactory.ts index a8f10e10..1faa9897 100644 --- a/front/src/WebRtc/JitsiFactory.ts +++ b/front/src/WebRtc/JitsiFactory.ts @@ -1,7 +1,9 @@ import { JITSI_URL } from "../Enum/EnvironmentVariable"; -import { CoWebsite, coWebsiteManager } from "./CoWebsiteManager"; +import { coWebsiteManager } from "./CoWebsiteManager"; import { requestedCameraState, requestedMicrophoneState } from "../Stores/MediaStore"; import { get } from "svelte/store"; +import type { CoWebsite } from "./CoWebsite/CoWesbite"; +import CancelablePromise from "cancelable-promise"; interface jitsiConfigInterface { startWithAudioMuted: boolean; @@ -134,89 +136,91 @@ class JitsiFactory { return slugify(instance.replace("/", "-") + "-" + roomName); } - public async start( + public start( roomName: string, playerName: string, jwt?: string, config?: object, interfaceConfig?: object, jitsiUrl?: string - ) { - const coWebsite = coWebsiteManager.searchJitsi(); + ): CancelablePromise { + return new CancelablePromise((resolve, reject, cancel) => { + // Jitsi meet external API maintains some data in local storage + // which is sent via the appData URL parameter when joining a + // conference. Problem is that this data grows indefinitely. Thus + // after some time the URLs get so huge that loading the iframe + // becomes slow and eventually breaks completely. Thus lets just + // clear jitsi local storage before starting a new conference. + window.localStorage.removeItem("jitsiLocalStorage"); - if (coWebsite) { - coWebsiteManager.closeCoWebsite(coWebsite); - } - - // Jitsi meet external API maintains some data in local storage - // which is sent via the appData URL parameter when joining a - // conference. Problem is that this data grows indefinitely. Thus - // after some time the URLs get so huge that loading the iframe - // becomes slow and eventually breaks completely. Thus lets just - // clear jitsi local storage before starting a new conference. - window.localStorage.removeItem("jitsiLocalStorage"); - - const domain = jitsiUrl || JITSI_URL; - if (domain === undefined) { - throw new Error("Missing JITSI_URL environment variable or jitsiUrl parameter in the map."); - } - await this.loadJitsiScript(domain); - - const options: JitsiOptions = { - roomName: roomName, - jwt: jwt, - width: "100%", - height: "100%", - parentNode: coWebsiteManager.getCoWebsiteBuffer(), - configOverwrite: mergeConfig(config), - interfaceConfigOverwrite: { ...defaultInterfaceConfig, ...interfaceConfig }, - }; - - if (!options.jwt) { - delete options.jwt; - } - - const doResolve = (): void => { - const iframe = coWebsiteManager.getCoWebsiteBuffer().querySelector('[id*="jitsi" i]'); - if (iframe && this.jitsiApi) { - const coWebsite = coWebsiteManager.addCoWebsiteFromIframe( - iframe, - false, - undefined, - undefined, - 0, - false, - true - ); - - this.jitsiApi.addListener("videoConferenceLeft", () => { - this.closeOrUnload(coWebsite); - }); - - this.jitsiApi.addListener("readyToClose", () => { - this.closeOrUnload(coWebsite); - }); + const domain = jitsiUrl || JITSI_URL; + if (domain === undefined) { + throw new Error("Missing JITSI_URL environment variable or jitsiUrl parameter in the map."); } - coWebsiteManager.resizeAllIframes(); - }; + const loadScript = this.loadJitsiScript(domain).then(() => { + const options: JitsiOptions = { + roomName: roomName, + jwt: jwt, + width: "100%", + height: "100%", + parentNode: coWebsiteManager.getCoWebsiteBuffer(), + configOverwrite: mergeConfig(config), + interfaceConfigOverwrite: { ...defaultInterfaceConfig, ...interfaceConfig }, + }; - this.jitsiApi = undefined; + if (!options.jwt) { + delete options.jwt; + } - options.onload = () => doResolve(); //we want for the iframe to be loaded before triggering animations. - setTimeout(() => doResolve(), 2000); //failsafe in case the iframe is deleted before loading or too long to load - this.jitsiApi = new window.JitsiMeetExternalAPI(domain, options); - this.jitsiApi.executeCommand("displayName", playerName); + const timemout = setTimeout(() => doResolve(), 2000); //failsafe in case the iframe is deleted before loading or too long to load - this.jitsiApi.addListener("audioMuteStatusChanged", this.audioCallback); - this.jitsiApi.addListener("videoMuteStatusChanged", this.videoCallback); + const doResolve = (): void => { + clearTimeout(timemout); + const iframe = coWebsiteManager + .getCoWebsiteBuffer() + .querySelector('[id*="jitsi" i]'); + if (iframe && this.jitsiApi) { + this.jitsiApi.addListener("videoConferenceLeft", () => { + this.closeOrUnload(); + }); + + this.jitsiApi.addListener("readyToClose", () => { + this.closeOrUnload(); + }); + + return resolve(iframe); + } + }; + + this.jitsiApi = undefined; + + options.onload = () => doResolve(); //we want for the iframe to be loaded before triggering animations. + this.jitsiApi = new window.JitsiMeetExternalAPI(domain, options); + this.jitsiApi.executeCommand("displayName", playerName); + + this.jitsiApi.addListener("audioMuteStatusChanged", this.audioCallback); + this.jitsiApi.addListener("videoMuteStatusChanged", this.videoCallback); + }); + + cancel(() => { + loadScript.cancel(); + }); + }); } - private closeOrUnload = function (coWebsite: CoWebsite) { - if (coWebsite.closable) { + private closeOrUnload = function () { + const coWebsite = coWebsiteManager.searchJitsi(); + if (!coWebsite) { + return; + } + + if (coWebsite.isClosable()) { coWebsiteManager.closeCoWebsite(coWebsite); } else { - coWebsiteManager.unloadCoWebsite(coWebsite); + coWebsiteManager.unloadCoWebsite(coWebsite).catch((err) => { + console.error("Cannot unload co-website from the Jitsi factory", err); + }); } }; @@ -229,8 +233,6 @@ class JitsiFactory { this.jitsiApi.addListener("videoMuteStatusChanged", this.videoCallback); const coWebsite = coWebsiteManager.searchJitsi(); - console.log("jitsi api ", this.jitsiApi); - console.log("iframe cowebsite", coWebsite?.iframe); if (!coWebsite) { this.destroy(); @@ -238,11 +240,11 @@ class JitsiFactory { } this.jitsiApi.addListener("videoConferenceLeft", () => { - this.closeOrUnload(coWebsite); + this.closeOrUnload(); }); this.jitsiApi.addListener("readyToClose", () => { - this.closeOrUnload(coWebsite); + this.closeOrUnload(); }); } @@ -280,8 +282,8 @@ class JitsiFactory { } } - private async loadJitsiScript(domain: string): Promise { - return new Promise((resolve, reject) => { + private loadJitsiScript(domain: string): CancelablePromise { + return new CancelablePromise((resolve, reject, cancel) => { if (this.jitsiScriptLoaded) { resolve(); return; @@ -300,6 +302,10 @@ class JitsiFactory { }; document.head.appendChild(jitsiScript); + + cancel(() => { + jitsiScript.remove(); + }); }); } } From 666b6df5884568682b9297f50152f63317248e67 Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Thu, 10 Feb 2022 15:37:04 +0100 Subject: [PATCH 34/42] Fix Jitsi co-website reloading --- front/src/Phaser/Game/GameScene.ts | 6 +++--- front/src/WebRtc/CoWebsite/JitsiCoWebsite.ts | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 443a6256..2f661320 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -2156,9 +2156,9 @@ ${escapedMessage} ); const jitsiUrl = allProps.get(GameMapProperties.JITSI_URL) as string | undefined; - coWebsite.setJitsiLoadPromise( - jitsiFactory.start(roomName, this.playerName, jwt, jitsiConfig, jitsiInterfaceConfig, jitsiUrl) - ); + coWebsite.setJitsiLoadPromise(() => { + return jitsiFactory.start(roomName, this.playerName, jwt, jitsiConfig, jitsiInterfaceConfig, jitsiUrl); + }); coWebsiteManager.loadCoWebsite(coWebsite).catch((err) => { console.error(err); diff --git a/front/src/WebRtc/CoWebsite/JitsiCoWebsite.ts b/front/src/WebRtc/CoWebsite/JitsiCoWebsite.ts index 66491569..654bb924 100644 --- a/front/src/WebRtc/CoWebsite/JitsiCoWebsite.ts +++ b/front/src/WebRtc/CoWebsite/JitsiCoWebsite.ts @@ -5,7 +5,7 @@ import { jitsiFactory } from "../JitsiFactory"; import { SimpleCoWebsite } from "./SimpleCoWebsite"; export class JitsiCoWebsite extends SimpleCoWebsite { - private jitsiLoadPromise?: CancelablePromise; + private jitsiLoadPromise?: () => CancelablePromise; constructor(url: URL, allowApi?: boolean, allowPolicy?: string, widthPercent?: number, closable?: boolean) { const coWebsite = coWebsiteManager.searchJitsi(); @@ -17,7 +17,7 @@ export class JitsiCoWebsite extends SimpleCoWebsite { super(url, allowApi, allowPolicy, widthPercent, closable); } - setJitsiLoadPromise(promise: CancelablePromise): void { + setJitsiLoadPromise(promise: () => CancelablePromise): void { this.jitsiLoadPromise = promise; } @@ -32,7 +32,7 @@ export class JitsiCoWebsite extends SimpleCoWebsite { return reject("Undefined Jitsi start callback"); } - const jitsiLoading = this.jitsiLoadPromise + const jitsiLoading = this.jitsiLoadPromise() .then((iframe) => { this.iframe = iframe; this.iframe.classList.add("pixel"); From 460d67534ad42e4b1c3b336fa395710334ade16e Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Thu, 10 Feb 2022 17:02:10 +0100 Subject: [PATCH 35/42] Move all game scene game map properties listeners --- .../CoWebsiteThumbnailSlot.svelte | 6 +- .../Phaser/Game/GameMapPropertiesListener.ts | 130 ++++++++++++++++ front/src/Phaser/Game/GameScene.ts | 142 +----------------- front/src/Stores/CoWebsiteStore.ts | 7 +- front/src/WebRtc/CoWebsite/JitsiCoWebsite.ts | 12 -- front/src/WebRtc/CoWebsiteManager.ts | 6 +- front/src/WebRtc/JitsiFactory.ts | 35 +---- 7 files changed, 149 insertions(+), 189 deletions(-) diff --git a/front/src/Components/EmbedScreens/CoWebsiteThumbnailSlot.svelte b/front/src/Components/EmbedScreens/CoWebsiteThumbnailSlot.svelte index 2043315b..d1d8c81a 100644 --- a/front/src/Components/EmbedScreens/CoWebsiteThumbnailSlot.svelte +++ b/front/src/Components/EmbedScreens/CoWebsiteThumbnailSlot.svelte @@ -2,9 +2,10 @@ import { onMount } from "svelte"; import { ICON_URL } from "../../Enum/EnvironmentVariable"; - import { coWebsitesNotAsleep, mainCoWebsite, jitsiCoWebsite } from "../../Stores/CoWebsiteStore"; + import { coWebsitesNotAsleep, mainCoWebsite } from "../../Stores/CoWebsiteStore"; import { highlightedEmbedScreen } from "../../Stores/EmbedScreensStore"; import type { CoWebsite } from "../../WebRtc/CoWebsite/CoWesbite"; + import { JitsiCoWebsite } from "../../WebRtc/CoWebsite/JitsiCoWebsite"; import { iframeStates } from "../../WebRtc/CoWebsiteManager"; import { coWebsiteManager } from "../../WebRtc/CoWebsiteManager"; @@ -15,10 +16,9 @@ let icon: HTMLImageElement; let iconLoaded = false; let state = coWebsite.getStateSubscriber(); - let isJitsi: boolean = false; + let isJitsi: boolean = coWebsite instanceof JitsiCoWebsite; onMount(() => { - isJitsi = Boolean($jitsiCoWebsite && $jitsiCoWebsite.getId() === coWebsite.getId()); icon.src = isJitsi ? "/resources/logos/meet.svg" : `${ICON_URL}/icon?url=${coWebsite.getUrl().hostname}&size=64..96..256&fallback_icon_color=14304c`; diff --git a/front/src/Phaser/Game/GameMapPropertiesListener.ts b/front/src/Phaser/Game/GameMapPropertiesListener.ts index 22073610..65de00f8 100644 --- a/front/src/Phaser/Game/GameMapPropertiesListener.ts +++ b/front/src/Phaser/Game/GameMapPropertiesListener.ts @@ -10,6 +10,12 @@ import type { ITiledMapLayer } from "../Map/ITiledMap"; import { GameMapProperties } from "./GameMapProperties"; import type { CoWebsite } from "../../WebRtc/CoWebsite/CoWesbite"; import { SimpleCoWebsite } from "../../WebRtc/CoWebsite/SimpleCoWebsite"; +import { jitsiFactory } from "../../WebRtc/JitsiFactory"; +import { JITSI_PRIVATE_MODE, JITSI_URL } from "../../Enum/EnvironmentVariable"; +import { JitsiCoWebsite } from "../../WebRtc/CoWebsite/JitsiCoWebsite"; +import { audioManagerFileStore, audioManagerVisibilityStore } from "../../Stores/AudioManagerStore"; +import { iframeListener } from "../../Api/IframeListener"; +import { Room } from "../../Connexion/Room"; interface OpenCoWebsite { actionId: string; @@ -23,6 +29,7 @@ export class GameMapPropertiesListener { constructor(private scene: GameScene, private gameMap: GameMap) {} register() { + // Website on new tab this.gameMap.onPropertyChange(GameMapProperties.OPEN_TAB, (newValue, oldValue, allProps) => { if (newValue === undefined) { layoutManagerActionStore.removeAction("openTab"); @@ -48,6 +55,129 @@ export class GameMapPropertiesListener { } }); + // Jitsi room + this.gameMap.onPropertyChange(GameMapProperties.JITSI_ROOM, (newValue, oldValue, allProps) => { + if (newValue === undefined) { + layoutManagerActionStore.removeAction("jitsi"); + coWebsiteManager.getCoWebsites().forEach((coWebsite) => { + if (coWebsite instanceof JitsiCoWebsite) { + coWebsiteManager.closeCoWebsite(coWebsite); + } + }); + } else { + const openJitsiRoomFunction = () => { + const roomName = jitsiFactory.getRoomName(newValue.toString(), this.scene.instance); + const jitsiUrl = allProps.get(GameMapProperties.JITSI_URL) as string | undefined; + + if (JITSI_PRIVATE_MODE && !jitsiUrl) { + const adminTag = allProps.get(GameMapProperties.JITSI_ADMIN_ROOM_TAG) as string | undefined; + + this.scene.connection?.emitQueryJitsiJwtMessage(roomName, adminTag); + } else { + let domain = jitsiUrl || JITSI_URL; + if (domain === undefined) { + throw new Error("Missing JITSI_URL environment variable or jitsiUrl parameter in the map."); + } + + if (domain.substring(0, 7) !== "http://" && domain.substring(0, 8) !== "https://") { + domain = `${location.protocol}//${domain}`; + } + + const coWebsite = new JitsiCoWebsite(new URL(domain), false, undefined, undefined, false); + + coWebsiteManager.addCoWebsiteToStore(coWebsite, 0); + this.scene.initialiseJitsi(coWebsite, roomName, undefined); + } + layoutManagerActionStore.removeAction("jitsi"); + }; + + const jitsiTriggerValue = allProps.get(GameMapProperties.JITSI_TRIGGER); + const forceTrigger = localUserStore.getForceCowebsiteTrigger(); + if (forceTrigger || jitsiTriggerValue === ON_ACTION_TRIGGER_BUTTON) { + let message = allProps.get(GameMapProperties.JITSI_TRIGGER_MESSAGE); + if (message === undefined) { + message = "Press SPACE or touch here to enter Jitsi Meet room"; + } + layoutManagerActionStore.addAction({ + uuid: "jitsi", + type: "message", + message: message, + callback: () => openJitsiRoomFunction(), + userInputManager: this.scene.userInputManager, + }); + } else { + openJitsiRoomFunction(); + } + } + }); + + this.gameMap.onPropertyChange(GameMapProperties.EXIT_SCENE_URL, (newValue, oldValue) => { + if (newValue) { + this.scene + .onMapExit( + Room.getRoomPathFromExitSceneUrl( + newValue as string, + window.location.toString(), + this.scene.MapUrlFile + ) + ) + .catch((e) => console.error(e)); + } else { + setTimeout(() => { + layoutManagerActionStore.removeAction("roomAccessDenied"); + }, 2000); + } + }); + + this.gameMap.onPropertyChange(GameMapProperties.EXIT_URL, (newValue, oldValue) => { + if (newValue) { + this.scene + .onMapExit(Room.getRoomPathFromExitUrl(newValue as string, window.location.toString())) + .catch((e) => console.error(e)); + } else { + setTimeout(() => { + layoutManagerActionStore.removeAction("roomAccessDenied"); + }, 2000); + } + }); + + this.gameMap.onPropertyChange(GameMapProperties.SILENT, (newValue, oldValue) => { + if (newValue === undefined || newValue === false || newValue === "") { + this.scene.connection?.setSilent(false); + this.scene.CurrentPlayer.noSilent(); + } else { + this.scene.connection?.setSilent(true); + this.scene.CurrentPlayer.isSilent(); + } + }); + + this.gameMap.onPropertyChange(GameMapProperties.PLAY_AUDIO, (newValue, oldValue, allProps) => { + const volume = allProps.get(GameMapProperties.AUDIO_VOLUME) as number | undefined; + const loop = allProps.get(GameMapProperties.AUDIO_LOOP) as boolean | undefined; + newValue === undefined + ? audioManagerFileStore.unloadAudio() + : audioManagerFileStore.playAudio(newValue, this.scene.getMapDirUrl(), volume, loop); + audioManagerVisibilityStore.set(!(newValue === undefined)); + }); + + // TODO: This legacy property should be removed at some point + this.gameMap.onPropertyChange(GameMapProperties.PLAY_AUDIO_LOOP, (newValue, oldValue) => { + newValue === undefined + ? audioManagerFileStore.unloadAudio() + : audioManagerFileStore.playAudio(newValue, this.scene.getMapDirUrl(), undefined, true); + audioManagerVisibilityStore.set(!(newValue === undefined)); + }); + + // TODO: Legacy functionnality replace by layer change + this.gameMap.onPropertyChange(GameMapProperties.ZONE, (newValue, oldValue) => { + if (oldValue) { + iframeListener.sendLeaveEvent(oldValue as string); + } + if (newValue) { + iframeListener.sendEnterEvent(newValue as string); + } + }); + // Open a new co-website by the property. this.gameMap.onEnterLayer((newLayers) => { const handler = () => { diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index 2f661320..78e82a30 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -20,15 +20,8 @@ import { EmbeddedWebsiteManager } from "./EmbeddedWebsiteManager"; import { lazyLoadPlayerCharacterTextures, loadCustomTexture } from "../Entity/PlayerTexturesLoadingManager"; import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager"; -import { ON_ACTION_TRIGGER_BUTTON } from "../../WebRtc/LayoutManager"; import { iframeListener } from "../../Api/IframeListener"; -import { - DEBUG_MODE, - JITSI_PRIVATE_MODE, - JITSI_URL, - MAX_PER_GROUP, - POSITION_DELAY, -} from "../../Enum/EnvironmentVariable"; +import { DEBUG_MODE, JITSI_URL, MAX_PER_GROUP, POSITION_DELAY } from "../../Enum/EnvironmentVariable"; import { ProtobufClientUtils } from "../../Network/ProtobufClientUtils"; import { Room } from "../../Connexion/Room"; import { jitsiFactory } from "../../WebRtc/JitsiFactory"; @@ -82,7 +75,7 @@ import { emoteStore, emoteMenuStore } from "../../Stores/EmoteStore"; import { userIsAdminStore } from "../../Stores/GameStore"; import { contactPageStore } from "../../Stores/MenuStore"; import type { WasCameraUpdatedEvent } from "../../Api/Events/WasCameraUpdatedEvent"; -import { audioManagerFileStore, audioManagerVisibilityStore } from "../../Stores/AudioManagerStore"; +import { audioManagerFileStore } from "../../Stores/AudioManagerStore"; import EVENT_TYPE = Phaser.Scenes.Events; import Texture = Phaser.Textures.Texture; @@ -636,7 +629,6 @@ export class GameScene extends DirtyScene { ); new GameMapPropertiesListener(this, this.gameMap).register(); - this.triggerOnMapLayerPropertyChange(); if (!this.room.isDisconnected()) { this.scene.sleep(); @@ -815,7 +807,7 @@ export class GameScene extends DirtyScene { const coWebsite = new JitsiCoWebsite(new URL(domain), false, undefined, undefined, false); coWebsiteManager.addCoWebsiteToStore(coWebsite, 0); - this.startJitsi(coWebsite, message.jitsiRoom, message.jwt); + this.initialiseJitsi(coWebsite, message.jitsiRoom, message.jwt); }); this.messageSubscription = this.connection.worldFullMessageStream.subscribe((message) => { @@ -952,116 +944,6 @@ export class GameScene extends DirtyScene { } } - private triggerOnMapLayerPropertyChange() { - this.gameMap.onPropertyChange(GameMapProperties.EXIT_SCENE_URL, (newValue, oldValue) => { - if (newValue) { - this.onMapExit( - Room.getRoomPathFromExitSceneUrl(newValue as string, window.location.toString(), this.MapUrlFile) - ).catch((e) => console.error(e)); - } else { - setTimeout(() => { - layoutManagerActionStore.removeAction("roomAccessDenied"); - }, 2000); - } - }); - this.gameMap.onPropertyChange(GameMapProperties.EXIT_URL, (newValue, oldValue) => { - if (newValue) { - this.onMapExit(Room.getRoomPathFromExitUrl(newValue as string, window.location.toString())).catch((e) => - console.error(e) - ); - } else { - setTimeout(() => { - layoutManagerActionStore.removeAction("roomAccessDenied"); - }, 2000); - } - }); - - this.gameMap.onPropertyChange(GameMapProperties.JITSI_ROOM, (newValue, oldValue, allProps) => { - if (newValue === undefined) { - layoutManagerActionStore.removeAction("jitsi"); - this.stopJitsi(); - } else { - const openJitsiRoomFunction = () => { - const roomName = jitsiFactory.getRoomName(newValue.toString(), this.instance); - const jitsiUrl = allProps.get(GameMapProperties.JITSI_URL) as string | undefined; - - if (JITSI_PRIVATE_MODE && !jitsiUrl) { - const adminTag = allProps.get(GameMapProperties.JITSI_ADMIN_ROOM_TAG) as string | undefined; - - this.connection?.emitQueryJitsiJwtMessage(roomName, adminTag); - } else { - let domain = jitsiUrl || JITSI_URL; - if (domain === undefined) { - throw new Error("Missing JITSI_URL environment variable or jitsiUrl parameter in the map."); - } - - if (domain.substring(0, 7) !== "http://" && domain.substring(0, 8) !== "https://") { - domain = `${location.protocol}//${domain}`; - } - - const coWebsite = new JitsiCoWebsite(new URL(domain), false, undefined, undefined, false); - - coWebsiteManager.addCoWebsiteToStore(coWebsite, 0); - this.startJitsi(coWebsite, roomName, undefined); - } - layoutManagerActionStore.removeAction("jitsi"); - }; - - const jitsiTriggerValue = allProps.get(GameMapProperties.JITSI_TRIGGER); - const forceTrigger = localUserStore.getForceCowebsiteTrigger(); - if (forceTrigger || jitsiTriggerValue === ON_ACTION_TRIGGER_BUTTON) { - let message = allProps.get(GameMapProperties.JITSI_TRIGGER_MESSAGE); - if (message === undefined) { - message = "Press SPACE or touch here to enter Jitsi Meet room"; - } - layoutManagerActionStore.addAction({ - uuid: "jitsi", - type: "message", - message: message, - callback: () => openJitsiRoomFunction(), - userInputManager: this.userInputManager, - }); - } else { - openJitsiRoomFunction(); - } - } - }); - this.gameMap.onPropertyChange(GameMapProperties.SILENT, (newValue, oldValue) => { - if (newValue === undefined || newValue === false || newValue === "") { - this.connection?.setSilent(false); - this.CurrentPlayer.noSilent(); - } else { - this.connection?.setSilent(true); - this.CurrentPlayer.isSilent(); - } - }); - this.gameMap.onPropertyChange(GameMapProperties.PLAY_AUDIO, (newValue, oldValue, allProps) => { - const volume = allProps.get(GameMapProperties.AUDIO_VOLUME) as number | undefined; - const loop = allProps.get(GameMapProperties.AUDIO_LOOP) as boolean | undefined; - newValue === undefined - ? audioManagerFileStore.unloadAudio() - : audioManagerFileStore.playAudio(newValue, this.getMapDirUrl(), volume, loop); - audioManagerVisibilityStore.set(!(newValue === undefined)); - }); - // TODO: This legacy property should be removed at some point - this.gameMap.onPropertyChange(GameMapProperties.PLAY_AUDIO_LOOP, (newValue, oldValue) => { - newValue === undefined - ? audioManagerFileStore.unloadAudio() - : audioManagerFileStore.playAudio(newValue, this.getMapDirUrl(), undefined, true); - audioManagerVisibilityStore.set(!(newValue === undefined)); - }); - - // TODO: Legacy functionnality replace by layer change - this.gameMap.onPropertyChange(GameMapProperties.ZONE, (newValue, oldValue) => { - if (oldValue) { - iframeListener.sendLeaveEvent(oldValue as string); - } - if (newValue) { - iframeListener.sendEnterEvent(newValue as string); - } - }); - } - private listenToIframeEvents(): void { this.iframeSubscriptionList = []; this.iframeSubscriptionList.push( @@ -1417,7 +1299,7 @@ ${escapedMessage} //Create new colliders with the new GameMap this.createCollisionWithPlayer(); //Create new trigger with the new GameMap - this.triggerOnMapLayerPropertyChange(); + new GameMapPropertiesListener(this, this.gameMap).register(); resolve(newFirstgid); }); }); @@ -1533,11 +1415,11 @@ ${escapedMessage} this.markDirty(); } - private getMapDirUrl(): string { - return this.MapUrlFile.substr(0, this.MapUrlFile.lastIndexOf("/")); + public getMapDirUrl(): string { + return this.MapUrlFile.substring(0, this.MapUrlFile.lastIndexOf("/")); } - private async onMapExit(roomUrl: URL) { + public async onMapExit(roomUrl: URL) { if (this.mapTransitioning) return; this.mapTransitioning = true; @@ -1603,7 +1485,6 @@ ${escapedMessage} iframeListener.unregisterScript(script); } - this.stopJitsi(); audioManagerFileStore.unloadAudio(); // We are completely destroying the current scene to avoid using a half-backed instance when coming back to the same map. this.connection?.closeConnection(); @@ -2144,7 +2025,7 @@ ${escapedMessage} mediaManager.hideMyCamera(); } - public startJitsi(coWebsite: JitsiCoWebsite, roomName: string, jwt?: string): void { + public initialiseJitsi(coWebsite: JitsiCoWebsite, roomName: string, jwt?: string): void { const allProps = this.gameMap.getCurrentProperties(); const jitsiConfig = this.safeParseJSONstring( allProps.get(GameMapProperties.JITSI_CONFIG) as string | undefined, @@ -2167,13 +2048,6 @@ ${escapedMessage} analyticsClient.enteredJitsi(roomName, this.room.id); } - public stopJitsi(): void { - const coWebsite = coWebsiteManager.searchJitsi(); - if (coWebsite) { - coWebsiteManager.closeCoWebsite(coWebsite); - } - } - //todo: put this into an 'orchestrator' scene (EntryScene?) private bannedUser() { this.cleanupClosingScene(); diff --git a/front/src/Stores/CoWebsiteStore.ts b/front/src/Stores/CoWebsiteStore.ts index 7f922db4..c4ed2f65 100644 --- a/front/src/Stores/CoWebsiteStore.ts +++ b/front/src/Stores/CoWebsiteStore.ts @@ -1,6 +1,5 @@ -import { derived, get, writable } from "svelte/store"; +import { derived, writable } from "svelte/store"; import type { CoWebsite } from "../WebRtc/CoWebsite/CoWesbite"; -import { JitsiCoWebsite } from "../WebRtc/CoWebsite/JitsiCoWebsite"; function createCoWebsiteStore() { const { subscribe, set, update } = writable(Array()); @@ -50,7 +49,3 @@ export const coWebsitesNotAsleep = derived([coWebsites], ([$coWebsites]) => export const mainCoWebsite = derived([coWebsites], ([$coWebsites]) => $coWebsites.find((coWebsite) => coWebsite.getState() !== "asleep") ); - -export const jitsiCoWebsite = derived([coWebsites], ([$coWebsites]) => - $coWebsites.find((coWebsite) => coWebsite instanceof JitsiCoWebsite) -); diff --git a/front/src/WebRtc/CoWebsite/JitsiCoWebsite.ts b/front/src/WebRtc/CoWebsite/JitsiCoWebsite.ts index 654bb924..a09b2bcb 100644 --- a/front/src/WebRtc/CoWebsite/JitsiCoWebsite.ts +++ b/front/src/WebRtc/CoWebsite/JitsiCoWebsite.ts @@ -1,22 +1,11 @@ import CancelablePromise from "cancelable-promise"; import { gameManager } from "../../Phaser/Game/GameManager"; -import { coWebsiteManager } from "../CoWebsiteManager"; import { jitsiFactory } from "../JitsiFactory"; import { SimpleCoWebsite } from "./SimpleCoWebsite"; export class JitsiCoWebsite extends SimpleCoWebsite { private jitsiLoadPromise?: () => CancelablePromise; - constructor(url: URL, allowApi?: boolean, allowPolicy?: string, widthPercent?: number, closable?: boolean) { - const coWebsite = coWebsiteManager.searchJitsi(); - - if (coWebsite) { - coWebsiteManager.closeCoWebsite(coWebsite); - } - - super(url, allowApi, allowPolicy, widthPercent, closable); - } - setJitsiLoadPromise(promise: () => CancelablePromise): void { this.jitsiLoadPromise = promise; } @@ -26,7 +15,6 @@ export class JitsiCoWebsite extends SimpleCoWebsite { this.state.set("loading"); gameManager.getCurrentGameScene().disableMediaBehaviors(); - jitsiFactory.restart(); if (!this.jitsiLoadPromise) { return reject("Undefined Jitsi start callback"); diff --git a/front/src/WebRtc/CoWebsiteManager.ts b/front/src/WebRtc/CoWebsiteManager.ts index f67b6e18..8e0b85ed 100644 --- a/front/src/WebRtc/CoWebsiteManager.ts +++ b/front/src/WebRtc/CoWebsiteManager.ts @@ -1,7 +1,7 @@ import { HtmlUtils } from "./HtmlUtils"; import { Subject } from "rxjs"; import { waScaleManager } from "../Phaser/Services/WaScaleManager"; -import { coWebsites, coWebsitesNotAsleep, jitsiCoWebsite, mainCoWebsite } from "../Stores/CoWebsiteStore"; +import { coWebsites, coWebsitesNotAsleep, mainCoWebsite } from "../Stores/CoWebsiteStore"; import { get } from "svelte/store"; import { embedScreenLayout, highlightedEmbedScreen } from "../Stores/EmbedScreensStore"; import { isMediaBreakpointDown } from "../Utils/BreakpointsUtils"; @@ -565,10 +565,6 @@ class CoWebsiteManager { this.resizeAllIframes(); } - public searchJitsi(): CoWebsite | undefined { - return get(jitsiCoWebsite); - } - public addCoWebsiteToStore(coWebsite: CoWebsite, position: number | undefined) { const coWebsitePosition = position === undefined ? get(coWebsites).length : position; coWebsites.add(coWebsite, coWebsitePosition); diff --git a/front/src/WebRtc/JitsiFactory.ts b/front/src/WebRtc/JitsiFactory.ts index 1faa9897..d328a72d 100644 --- a/front/src/WebRtc/JitsiFactory.ts +++ b/front/src/WebRtc/JitsiFactory.ts @@ -2,7 +2,6 @@ import { JITSI_URL } from "../Enum/EnvironmentVariable"; import { coWebsiteManager } from "./CoWebsiteManager"; import { requestedCameraState, requestedMicrophoneState } from "../Stores/MediaStore"; import { get } from "svelte/store"; -import type { CoWebsite } from "./CoWebsite/CoWesbite"; import CancelablePromise from "cancelable-promise"; interface jitsiConfigInterface { @@ -180,13 +179,14 @@ class JitsiFactory { const iframe = coWebsiteManager .getCoWebsiteBuffer() .querySelector('[id*="jitsi" i]'); + if (iframe && this.jitsiApi) { this.jitsiApi.addListener("videoConferenceLeft", () => { - this.closeOrUnload(); + this.closeOrUnload(iframe); }); this.jitsiApi.addListener("readyToClose", () => { - this.closeOrUnload(); + this.closeOrUnload(iframe); }); return resolve(iframe); @@ -209,8 +209,9 @@ class JitsiFactory { }); } - private closeOrUnload = function () { - const coWebsite = coWebsiteManager.searchJitsi(); + private closeOrUnload = function (iframe: HTMLIFrameElement) { + const coWebsite = coWebsiteManager.getCoWebsites().find((coWebsite) => coWebsite.getIframe() === iframe); + if (!coWebsite) { return; } @@ -224,30 +225,6 @@ class JitsiFactory { } }; - public restart() { - if (!this.jitsiApi) { - return; - } - - this.jitsiApi.addListener("audioMuteStatusChanged", this.audioCallback); - this.jitsiApi.addListener("videoMuteStatusChanged", this.videoCallback); - - const coWebsite = coWebsiteManager.searchJitsi(); - - if (!coWebsite) { - this.destroy(); - return; - } - - this.jitsiApi.addListener("videoConferenceLeft", () => { - this.closeOrUnload(); - }); - - this.jitsiApi.addListener("readyToClose", () => { - this.closeOrUnload(); - }); - } - public stop() { if (!this.jitsiApi) { return; From b0c0d22f2584d805d8ebe0cccd7d7e955a0d31e1 Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Thu, 10 Feb 2022 17:30:03 +0100 Subject: [PATCH 36/42] Translate game map properties trigger messages --- front/src/Phaser/Game/GameMapPropertiesListener.ts | 7 ++++--- front/src/i18n/en-US/index.ts | 2 ++ front/src/i18n/en-US/trigger.ts | 9 +++++++++ front/src/i18n/fr-FR/index.ts | 2 ++ front/src/i18n/fr-FR/menu.ts | 2 +- front/src/i18n/fr-FR/trigger.ts | 9 +++++++++ 6 files changed, 27 insertions(+), 4 deletions(-) create mode 100644 front/src/i18n/en-US/trigger.ts create mode 100644 front/src/i18n/fr-FR/trigger.ts diff --git a/front/src/Phaser/Game/GameMapPropertiesListener.ts b/front/src/Phaser/Game/GameMapPropertiesListener.ts index 65de00f8..103cc4bc 100644 --- a/front/src/Phaser/Game/GameMapPropertiesListener.ts +++ b/front/src/Phaser/Game/GameMapPropertiesListener.ts @@ -16,6 +16,7 @@ import { JitsiCoWebsite } from "../../WebRtc/CoWebsite/JitsiCoWebsite"; import { audioManagerFileStore, audioManagerVisibilityStore } from "../../Stores/AudioManagerStore"; import { iframeListener } from "../../Api/IframeListener"; import { Room } from "../../Connexion/Room"; +import LL from "../../i18n/i18n-svelte"; interface OpenCoWebsite { actionId: string; @@ -40,7 +41,7 @@ export class GameMapPropertiesListener { if (forceTrigger || openWebsiteTriggerValue === ON_ACTION_TRIGGER_BUTTON) { let message = allProps.get(GameMapProperties.OPEN_WEBSITE_TRIGGER_MESSAGE); if (message === undefined) { - message = "Press SPACE or touch here to open web site in new tab"; + message = get(LL).trigger.newTab(); } layoutManagerActionStore.addAction({ uuid: "openTab", @@ -96,7 +97,7 @@ export class GameMapPropertiesListener { if (forceTrigger || jitsiTriggerValue === ON_ACTION_TRIGGER_BUTTON) { let message = allProps.get(GameMapProperties.JITSI_TRIGGER_MESSAGE); if (message === undefined) { - message = "Press SPACE or touch here to enter Jitsi Meet room"; + message = get(LL).trigger.jitsiRoom(); } layoutManagerActionStore.addAction({ uuid: "jitsi", @@ -265,7 +266,7 @@ export class GameMapPropertiesListener { websiteTriggerProperty === ON_ACTION_TRIGGER_BUTTON ) { if (!websiteTriggerMessageProperty) { - websiteTriggerMessageProperty = "Press SPACE or touch here to open web site"; + websiteTriggerMessageProperty = get(LL).trigger.cowebsite(); } this.coWebsitesActionTriggerByLayer.set(layer, actionId); diff --git a/front/src/i18n/en-US/index.ts b/front/src/i18n/en-US/index.ts index 2d3ac74a..b1a0e422 100644 --- a/front/src/i18n/en-US/index.ts +++ b/front/src/i18n/en-US/index.ts @@ -11,6 +11,7 @@ import menu from "./menu"; import report from "./report"; import warning from "./warning"; import emoji from "./emoji"; +import trigger from "./trigger"; const en_US: BaseTranslation = { language: "English", @@ -27,6 +28,7 @@ const en_US: BaseTranslation = { report, warning, emoji, + trigger, }; export default en_US; diff --git a/front/src/i18n/en-US/trigger.ts b/front/src/i18n/en-US/trigger.ts new file mode 100644 index 00000000..40e84bf4 --- /dev/null +++ b/front/src/i18n/en-US/trigger.ts @@ -0,0 +1,9 @@ +import type { BaseTranslation } from "../i18n-types"; + +const trigger: BaseTranslation = { + cowebsite: "Press SPACE or touch here to open web site", + jitsiRoom: "Press SPACE or touch here to enter Jitsi Meet room", + newTab: "Press SPACE or touch here to open web site in new tab", +}; + +export default trigger; diff --git a/front/src/i18n/fr-FR/index.ts b/front/src/i18n/fr-FR/index.ts index b9f91b13..5c9c587e 100644 --- a/front/src/i18n/fr-FR/index.ts +++ b/front/src/i18n/fr-FR/index.ts @@ -12,6 +12,7 @@ import menu from "./menu"; import report from "./report"; import warning from "./warning"; import woka from "./woka"; +import trigger from "./trigger"; const fr_FR: Translation = { ...en_US, @@ -29,6 +30,7 @@ const fr_FR: Translation = { report, warning, emoji, + trigger, }; export default fr_FR; diff --git a/front/src/i18n/fr-FR/menu.ts b/front/src/i18n/fr-FR/menu.ts index 54481ec8..1ca20fd7 100644 --- a/front/src/i18n/fr-FR/menu.ts +++ b/front/src/i18n/fr-FR/menu.ts @@ -63,7 +63,7 @@ const menu: NonNullable = { }, fullscreen: "Plein écran", notifications: "Notifications", - cowebsiteTrigger: "Demander toujours avant d'ouvrir des sites web et des salles de réunion Jitsi", + cowebsiteTrigger: "Demander toujours avant d'ouvrir des sites web et des salles de conférence Jitsi", ignoreFollowRequest: "Ignorer les demandes de suivi des autres utilisateurs", }, invite: { diff --git a/front/src/i18n/fr-FR/trigger.ts b/front/src/i18n/fr-FR/trigger.ts new file mode 100644 index 00000000..5940e92a --- /dev/null +++ b/front/src/i18n/fr-FR/trigger.ts @@ -0,0 +1,9 @@ +import type { Translation } from "../i18n-types"; + +const trigger: NonNullable = { + cowebsite: "Appuyez sur ESPACE ou ici pour ouvrir le site Web", + jitsiRoom: "Appuyez sur ESPACE ou ici pour entrer dans la salle conférence Jitsi", + newTab: "Appuyez sur ESPACE ou ici pour ouvrir le site Web dans un nouvel onglet", +}; + +export default trigger; From a5e0c2a9cf281a37683a5592d76c77b8e4e8c48e Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Thu, 10 Feb 2022 19:26:46 +0100 Subject: [PATCH 37/42] Add main cowebsite minimize indicator --- .../CoWebsiteThumbnailSlot.svelte | 29 ++++++++++++------- front/src/WebRtc/CoWebsiteManager.ts | 25 ++++++++-------- 2 files changed, 31 insertions(+), 23 deletions(-) diff --git a/front/src/Components/EmbedScreens/CoWebsiteThumbnailSlot.svelte b/front/src/Components/EmbedScreens/CoWebsiteThumbnailSlot.svelte index d1d8c81a..4ff59f5d 100644 --- a/front/src/Components/EmbedScreens/CoWebsiteThumbnailSlot.svelte +++ b/front/src/Components/EmbedScreens/CoWebsiteThumbnailSlot.svelte @@ -2,7 +2,7 @@ import { onMount } from "svelte"; import { ICON_URL } from "../../Enum/EnvironmentVariable"; - import { coWebsitesNotAsleep, mainCoWebsite } from "../../Stores/CoWebsiteStore"; + import { mainCoWebsite } from "../../Stores/CoWebsiteStore"; import { highlightedEmbedScreen } from "../../Stores/EmbedScreensStore"; import type { CoWebsite } from "../../WebRtc/CoWebsite/CoWesbite"; import { JitsiCoWebsite } from "../../WebRtc/CoWebsite/JitsiCoWebsite"; @@ -17,6 +17,7 @@ let iconLoaded = false; let state = coWebsite.getStateSubscriber(); let isJitsi: boolean = coWebsite instanceof JitsiCoWebsite; + const mainState = coWebsiteManager.getMainStateSubscriber(); onMount(() => { icon.src = isJitsi @@ -33,20 +34,23 @@ coWebsiteManager.goToMain(coWebsite); } else if ($mainCoWebsite) { if ($mainCoWebsite.getId() === coWebsite.getId()) { - const coWebsites = $coWebsitesNotAsleep; - const newMain = $highlightedEmbedScreen ?? coWebsites.length > 1 ? coWebsites[1] : undefined; - if (newMain && newMain.getId() !== $mainCoWebsite.getId()) { - coWebsiteManager.goToMain(newMain); - } else if (coWebsiteManager.getMainState() === iframeStates.closed) { + if (coWebsiteManager.getMainState() === iframeStates.closed) { coWebsiteManager.displayMain(); + } else if ($highlightedEmbedScreen?.type === "cowebsite") { + coWebsiteManager.goToMain($highlightedEmbedScreen.embed); } else { coWebsiteManager.hideMain(); } } else { - highlightedEmbedScreen.toggleHighlight({ - type: "cowebsite", - embed: coWebsite, - }); + if (coWebsiteManager.getMainState() === iframeStates.closed) { + coWebsiteManager.goToMain(coWebsite); + coWebsiteManager.displayMain(); + } else { + highlightedEmbedScreen.toggleHighlight({ + type: "cowebsite", + embed: coWebsite, + }); + } } } @@ -64,7 +68,10 @@ let isHighlight: boolean = false; let isMain: boolean = false; $: { - isMain = $mainCoWebsite !== undefined && $mainCoWebsite.getId() === coWebsite.getId(); + isMain = + $mainState === iframeStates.opened && + $mainCoWebsite !== undefined && + $mainCoWebsite.getId() === coWebsite.getId(); isHighlight = $highlightedEmbedScreen !== null && $highlightedEmbedScreen.type === "cowebsite" && diff --git a/front/src/WebRtc/CoWebsiteManager.ts b/front/src/WebRtc/CoWebsiteManager.ts index 8e0b85ed..a5c57ed6 100644 --- a/front/src/WebRtc/CoWebsiteManager.ts +++ b/front/src/WebRtc/CoWebsiteManager.ts @@ -2,7 +2,7 @@ import { HtmlUtils } from "./HtmlUtils"; import { Subject } from "rxjs"; import { waScaleManager } from "../Phaser/Services/WaScaleManager"; import { coWebsites, coWebsitesNotAsleep, mainCoWebsite } from "../Stores/CoWebsiteStore"; -import { get } from "svelte/store"; +import { get, Readable, Writable, writable } from "svelte/store"; import { embedScreenLayout, highlightedEmbedScreen } from "../Stores/EmbedScreensStore"; import { isMediaBreakpointDown } from "../Utils/BreakpointsUtils"; import { LayoutMode } from "./LayoutManager"; @@ -34,7 +34,7 @@ interface TouchMoveCoordinates { } class CoWebsiteManager { - private openedMain: iframeStates = iframeStates.closed; + private openedMain: Writable = writable(iframeStates.closed); private _onResize: Subject = new Subject(); public onResize = this._onResize.asObservable(); @@ -57,6 +57,10 @@ class CoWebsiteManager { }); public getMainState() { + return get(this.openedMain); + } + + public getMainStateSubscriber(): Readable { return this.openedMain; } @@ -324,7 +328,7 @@ class CoWebsiteManager { } this.cowebsiteDom.classList.add("closing"); this.cowebsiteDom.classList.remove("opened"); - this.openedMain = iframeStates.closed; + this.openedMain.set(iframeStates.closed); this.fire(); } @@ -332,7 +336,7 @@ class CoWebsiteManager { this.toggleFullScreenIcon(true); this.cowebsiteDom.classList.add("closing"); this.cowebsiteDom.classList.remove("opened"); - this.openedMain = iframeStates.closed; + this.openedMain.set(iframeStates.closed); this.resetStyleMain(); this.fire(); } @@ -386,14 +390,14 @@ class CoWebsiteManager { } this.cowebsiteDom.classList.add("opened"); - this.openedMain = iframeStates.loading; + this.openedMain.set(iframeStates.loading); } private openMain(): void { this.cowebsiteDom.addEventListener("transitionend", () => { this.resizeAllIframes(); }); - this.openedMain = iframeStates.opened; + this.openedMain.set(iframeStates.opened); } public resetStyleMain() { @@ -556,10 +560,7 @@ class CoWebsiteManager { mainCoWebsite.getId() !== coWebsite.getId() && mainCoWebsite.getState() !== "asleep" ) { - highlightedEmbedScreen.toggleHighlight({ - type: "cowebsite", - embed: mainCoWebsite, - }); + highlightedEmbedScreen.removeHighlight(); } this.resizeAllIframes(); @@ -587,7 +588,7 @@ class CoWebsiteManager { } // Check if the main is hide - if (this.getMainCoWebsite() && this.openedMain === iframeStates.closed) { + if (this.getMainCoWebsite() && this.getMainState() === iframeStates.closed) { this.displayMain(); } @@ -661,7 +662,7 @@ class CoWebsiteManager { } public getGameSize(): { width: number; height: number } { - if (this.openedMain === iframeStates.closed) { + if (this.getMainState() === iframeStates.closed) { return { width: window.innerWidth, height: window.innerHeight, From b7f4c0eecc15a6c7a726102fd223e085ef079205 Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Thu, 10 Feb 2022 19:29:41 +0100 Subject: [PATCH 38/42] Fix bad type base i18n translations --- front/src/i18n/de-DE/index.ts | 2 +- front/src/i18n/fr-FR/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/front/src/i18n/de-DE/index.ts b/front/src/i18n/de-DE/index.ts index ab628a4d..40b63558 100644 --- a/front/src/i18n/de-DE/index.ts +++ b/front/src/i18n/de-DE/index.ts @@ -14,7 +14,7 @@ import warning from "./warning"; import woka from "./woka"; const de_DE: Translation = { - ...en_US, + ...(en_US as Translation), language: "Deutsch", country: "Deutschland", audio, diff --git a/front/src/i18n/fr-FR/index.ts b/front/src/i18n/fr-FR/index.ts index 5c9c587e..77acbb4a 100644 --- a/front/src/i18n/fr-FR/index.ts +++ b/front/src/i18n/fr-FR/index.ts @@ -15,7 +15,7 @@ import woka from "./woka"; import trigger from "./trigger"; const fr_FR: Translation = { - ...en_US, + ...(en_US as Translation), language: "Français", country: "France", audio, From 5137190558c20f70a9bfd6607d17cfa5f73e95cb Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Fri, 11 Feb 2022 15:06:41 +0100 Subject: [PATCH 39/42] Change cowebsite closing animation --- .../EmbedScreens/CoWebsiteThumbnailSlot.svelte | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/front/src/Components/EmbedScreens/CoWebsiteThumbnailSlot.svelte b/front/src/Components/EmbedScreens/CoWebsiteThumbnailSlot.svelte index 4ff59f5d..b01dbf0a 100644 --- a/front/src/Components/EmbedScreens/CoWebsiteThumbnailSlot.svelte +++ b/front/src/Components/EmbedScreens/CoWebsiteThumbnailSlot.svelte @@ -219,7 +219,8 @@ } &:not(.vertical) { - animation: bounce 0.35s ease 6 alternate; + transition: all 300ms; + transform: translateY(0px); } &.vertical { @@ -240,7 +241,7 @@ &.displayed { &:not(.vertical) { - animation: activeThumbnail 300ms ease-in 0s forwards; + transform: translateY(-15px); } } @@ -269,16 +270,6 @@ } } - @keyframes activeThumbnail { - 0% { - transform: translateY(0); - } - - 100% { - transform: translateY(-15px); - } - } - @keyframes bounce { from { transform: translateY(0); From 664cce87b8c31be9b2274a880ef2e954b59933bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Fri, 11 Feb 2022 15:37:23 +0100 Subject: [PATCH 40/42] Improving rendering of share screen --- front/src/Components/Menu/GuestSubMenu.svelte | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/front/src/Components/Menu/GuestSubMenu.svelte b/front/src/Components/Menu/GuestSubMenu.svelte index 34fba1af..f24a3f90 100644 --- a/front/src/Components/Menu/GuestSubMenu.svelte +++ b/front/src/Components/Menu/GuestSubMenu.svelte @@ -28,6 +28,8 @@ } } + let canShare = navigator.canShare; + async function shareLink() { const shareData = { url: getLink() }; @@ -42,18 +44,21 @@
+ {#if (!canShare) } + {:else}

{$LL.menu.invite.description()}

+ {/if}

Select an entry point

-
+
- -
+ {#if !canShare} + {:else} -
-

{$LL.menu.invite.description()}

- - -
+
+

{$LL.menu.invite.description()}

+ + +
{/if}

Select an entry point

@@ -79,7 +79,7 @@ updateInputFieldValue(); }} /> - Walk automatically to my position + {$LL.menu.invite.walk_automatically_to_position()}
@@ -132,8 +132,8 @@ } @include media-breakpoint-up(lg) { - div.guest-main { - width: 100%; - } + div.guest-main { + width: 100%; + } } diff --git a/front/src/i18n/de-DE/menu.ts b/front/src/i18n/de-DE/menu.ts index 5fb7882d..5e695d95 100644 --- a/front/src/i18n/de-DE/menu.ts +++ b/front/src/i18n/de-DE/menu.ts @@ -70,6 +70,7 @@ const menu: NonNullable = { description: "Link zu diesem Raum teilen!", copy: "Kopieren", share: "Teilen", + walk_automatically_to_position: "Walk automatically to my position", }, globalMessage: { text: "Text", diff --git a/front/src/i18n/en-US/menu.ts b/front/src/i18n/en-US/menu.ts index b7c69935..0883fb15 100644 --- a/front/src/i18n/en-US/menu.ts +++ b/front/src/i18n/en-US/menu.ts @@ -70,6 +70,7 @@ const menu: BaseTranslation = { description: "Share the link of the room!", copy: "Copy", share: "Share", + walk_automatically_to_position: "Walk automatically to my position", }, globalMessage: { text: "Text", diff --git a/front/src/i18n/fr-FR/menu.ts b/front/src/i18n/fr-FR/menu.ts index 54481ec8..bf3ede7a 100644 --- a/front/src/i18n/fr-FR/menu.ts +++ b/front/src/i18n/fr-FR/menu.ts @@ -70,6 +70,7 @@ const menu: NonNullable = { description: "Partager le lien de la salle!", copy: "Copier", share: "Partager", + walk_automatically_to_position: "Marcher automatiquement jusqu'à ma position", }, globalMessage: { text: "Texte",