diff --git a/front/src/Phaser/Game/GameMap.ts b/front/src/Phaser/Game/GameMap.ts
index 8e377ae7..3df3cb01 100644
--- a/front/src/Phaser/Game/GameMap.ts
+++ b/front/src/Phaser/Game/GameMap.ts
@@ -121,7 +121,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[] = [];
@@ -333,6 +333,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/GameMapPropertiesListener.ts b/front/src/Phaser/Game/GameMapPropertiesListener.ts
index 497b6cbc..103cc4bc 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,17 +8,19 @@ 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 { 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";
+import LL from "../../i18n/i18n-svelte";
interface OpenCoWebsite {
actionId: string;
coWebsite?: CoWebsite;
- state: OpenCoWebsiteState;
}
export class GameMapPropertiesListener {
@@ -29,6 +30,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");
@@ -39,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",
@@ -54,6 +56,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 = get(LL).trigger.jitsiRoom();
+ }
+ 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 = () => {
@@ -106,49 +231,33 @@ 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);
- });
+ 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);
};
@@ -157,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);
@@ -170,21 +279,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
);
- const ObjectByLayer = this.coWebsitesOpenByLayer.get(layer);
+ coWebsiteOpen.coWebsite = coWebsite;
- if (ObjectByLayer) {
- ObjectByLayer.coWebsite = coWebsite;
- }
+ coWebsiteManager.addCoWebsiteToStore(coWebsite, websitePositionProperty);
}
if (!websiteTriggerProperty) {
@@ -228,12 +333,10 @@ export class GameMapPropertiesListener {
return;
}
- if (coWebsiteOpen.state === OpenCoWebsiteState.ASLEEP) {
- coWebsiteOpen.state = OpenCoWebsiteState.MUST_BE_CLOSE;
- }
+ const coWebsite = coWebsiteOpen.coWebsite;
- if (coWebsiteOpen.coWebsite !== undefined) {
- coWebsiteManager.closeCoWebsite(coWebsiteOpen.coWebsite).catch((e) => console.error(e));
+ 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 40baf0c5..87ca28af 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";
@@ -20,9 +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, 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";
@@ -76,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;
@@ -94,6 +93,9 @@ import { GameSceneUserInputHandler } from "../UserInput/GameSceneUserInputHandle
import { locale } from "../../i18n/i18n-svelte";
import { StringUtils } from "../../Utils/StringUtils";
import { startLayerNamesStore } from "../../Stores/StartLayerNamesStore";
+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;
@@ -564,7 +566,7 @@ export class GameScene extends DirtyScene {
this.pathfindingManager = new PathfindingManager(
this,
- this.gameMap.getCollisionsGrid(),
+ this.gameMap.getCollisionGrid(),
this.gameMap.getTileDimensions()
);
@@ -582,7 +584,7 @@ export class GameScene extends DirtyScene {
this.pathfindingManager = new PathfindingManager(
this,
- this.gameMap.getCollisionsGrid(),
+ this.gameMap.getCollisionGrid(),
this.gameMap.getTileDimensions()
);
@@ -633,7 +635,6 @@ export class GameScene extends DirtyScene {
);
new GameMapPropertiesListener(this, this.gameMap).register();
- this.triggerOnMapLayerPropertyChange();
if (!this.room.isDisconnected()) {
this.scene.sleep();
@@ -800,7 +801,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.initialiseJitsi(coWebsite, message.jitsiRoom, message.jwt);
});
this.messageSubscription = this.connection.worldFullMessageStream.subscribe((message) => {
@@ -937,103 +950,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 {
- this.startJitsi(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(
@@ -1266,13 +1182,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
);
@@ -1281,7 +1195,7 @@ ${escapedMessage}
}
return {
- id: coWebsite.iframe.id,
+ id: coWebsite.getId(),
};
});
@@ -1290,27 +1204,23 @@ ${escapedMessage}
return coWebsites.map((coWebsite: CoWebsite) => {
return {
- id: coWebsite.iframe.id,
+ id: coWebsite.getId(),
};
});
});
- 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", () => {
@@ -1395,7 +1305,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);
});
});
@@ -1508,14 +1418,15 @@ ${escapedMessage}
phaserLayers[i].setCollisionByProperty({ collides: true }, visible);
}
}
+ this.pathfindingManager.setCollisionGrid(this.gameMap.getCollisionGrid());
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;
@@ -1574,14 +1485,13 @@ ${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) {
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();
@@ -2136,7 +2046,7 @@ ${escapedMessage}
mediaManager.hideMyCamera();
}
- public startJitsi(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,
@@ -2148,20 +2058,15 @@ ${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(() => {
+ return jitsiFactory.start(roomName, this.playerName, jwt, jitsiConfig, jitsiInterfaceConfig, jitsiUrl);
});
- this.disableMediaBehaviors();
- analyticsClient.enteredJitsi(roomName, this.room.id);
- }
- public stopJitsi(): void {
- const coWebsite = coWebsiteManager.searchJitsi();
- if (coWebsite) {
- coWebsiteManager.closeCoWebsite(coWebsite).catch((e) => {
- console.error("Error during Jitsi co-website closing", e);
- });
- }
+ coWebsiteManager.loadCoWebsite(coWebsite).catch((err) => {
+ console.error(err);
+ });
+
+ analyticsClient.enteredJitsi(roomName, this.room.id);
}
//todo: put this into an 'orchestrator' scene (EntryScene?)
diff --git a/front/src/Stores/CoWebsiteStore.ts b/front/src/Stores/CoWebsiteStore.ts
index 4227c405..c4ed2f65 100644
--- a/front/src/Stores/CoWebsiteStore.ts
+++ b/front/src/Stores/CoWebsiteStore.ts
@@ -1,5 +1,5 @@
-import { derived, get, writable } from "svelte/store";
-import type { CoWebsite } from "../WebRtc/CoWebsiteManager";
+import { derived, writable } from "svelte/store";
+import type { CoWebsite } from "../WebRtc/CoWebsite/CoWesbite";
function createCoWebsiteStore() {
const { subscribe, set, update } = writable(Array
());
@@ -9,7 +9,7 @@ function createCoWebsiteStore() {
return {
subscribe,
add: (coWebsite: CoWebsite, position?: number) => {
- coWebsite.state.subscribe((value) => {
+ coWebsite.getStateSubscriber().subscribe((value) => {
update((currentArray) => currentArray);
});
@@ -31,7 +31,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 +43,9 @@ 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")
);
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/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/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..a09b2bcb
--- /dev/null
+++ b/front/src/WebRtc/CoWebsite/JitsiCoWebsite.ts
@@ -0,0 +1,49 @@
+import CancelablePromise from "cancelable-promise";
+import { gameManager } from "../../Phaser/Game/GameManager";
+import { jitsiFactory } from "../JitsiFactory";
+import { SimpleCoWebsite } from "./SimpleCoWebsite";
+
+export class JitsiCoWebsite extends SimpleCoWebsite {
+ private jitsiLoadPromise?: () => CancelablePromise;
+
+ setJitsiLoadPromise(promise: () => CancelablePromise): void {
+ this.jitsiLoadPromise = promise;
+ }
+
+ load(): CancelablePromise {
+ return new CancelablePromise((resolve, reject, cancel) => {
+ this.state.set("loading");
+
+ gameManager.getCurrentGameScene().disableMediaBehaviors();
+
+ 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 476526da..a5c57ed6 100644
--- a/front/src/WebRtc/CoWebsiteManager.ts
+++ b/front/src/WebRtc/CoWebsiteManager.ts
@@ -1,14 +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 { get, Readable, Writable, writable } 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 type { CoWebsite } from "./CoWebsite/CoWesbite";
+import type CancelablePromise from "cancelable-promise";
export enum iframeStates {
closed = 1,
@@ -34,30 +33,12 @@ 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;
+ private openedMain: Writable = writable(iframeStates.closed);
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;
@@ -76,6 +57,10 @@ class CoWebsiteManager {
});
public getMainState() {
+ return get(this.openedMain);
+ }
+
+ public getMainStateSubscriber(): Readable {
return this.openedMain;
}
@@ -147,13 +132,11 @@ class CoWebsiteManager {
throw new Error("Undefined main co-website on closing");
}
- if (coWebsite.closable) {
- this.closeCoWebsite(coWebsite).catch(() => {
- console.error("Error during closing a co-website by a button");
- });
+ if (coWebsite.isClosable()) {
+ this.closeCoWebsite(coWebsite);
} else {
- this.unloadCoWebsite(coWebsite).catch(() => {
- console.error("Error during unloading a co-website by a button");
+ this.unloadCoWebsite(coWebsite).catch((err) => {
+ console.error("Cannot unload co-website on click on close button", err);
});
}
});
@@ -241,7 +224,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);
});
@@ -257,7 +243,10 @@ class CoWebsiteManager {
return;
}
- coWebsite.iframe.style.display = "flex";
+ const iframe = coWebsite.getIframe();
+ if (iframe) {
+ iframe.style.display = "flex";
+ }
this.resizing = false;
});
@@ -270,7 +259,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 };
@@ -289,7 +281,10 @@ class CoWebsiteManager {
return;
}
- coWebsite.iframe.style.display = "flex";
+ const iframe = coWebsite.getIframe();
+ if (iframe) {
+ iframe.style.display = "flex";
+ }
this.resizing = false;
});
}
@@ -313,9 +308,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();
}
@@ -323,11 +321,14 @@ 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");
- this.openedMain = iframeStates.closed;
+ this.openedMain.set(iframeStates.closed);
this.fire();
}
@@ -335,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();
}
@@ -389,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() {
@@ -409,7 +410,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 {
@@ -429,7 +432,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 {
@@ -443,7 +448,7 @@ class CoWebsiteManager {
if (index === 0) {
id += "main";
} else {
- id += coWebsite.iframe.id;
+ id += coWebsite.getId();
}
const slot = HtmlUtils.getElementById(id);
@@ -460,60 +465,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();
}
}
@@ -526,7 +543,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) {
@@ -538,38 +557,21 @@ 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",
- embed: mainCoWebsite,
- });
+ highlightedEmbedScreen.removeHighlight();
}
this.resizeAllIframes();
}
- public searchJitsi(): CoWebsite | undefined {
- return get(coWebsites).find((coWebsite: CoWebsite) => coWebsite.jitsi);
- }
-
- 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);
@@ -578,210 +580,89 @@ 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): Promise {
+ 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
- if (this.getMainCoWebsite() && this.openedMain === iframeStates.closed) {
+ if (this.getMainCoWebsite() && this.getMainState() === iframeStates.closed) {
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 Promise((resolve, reject) => {
- 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();
- }
-
- this.currentOperationPromise = this.currentOperationPromise
- .then(() => Promise.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();
- });
- });
+ return coWebsiteLloading;
}
public unloadCoWebsite(coWebsite: CoWebsite): Promise {
- return new Promise((resolve, reject) => {
- this.removeHighlightCoWebsite(coWebsite);
+ 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();
+ coWebsites.add(coWebsite, get(coWebsites).length);
+ })
+ .catch(() => {
+ console.error();
+ });
+ }
- if (mainCoWebsite) {
- this.removeHighlightCoWebsite(mainCoWebsite);
- this.goToMain(mainCoWebsite);
- this.resizeAllIframes();
- } else {
- this.closeMain();
- }
+ public closeCoWebsite(coWebsite: CoWebsite): void {
+ if (get(coWebsites).length === 1) {
+ this.fire();
+ }
- coWebsites.add(coWebsite, get(coWebsites).length);
+ this.removeCoWebsiteFromStack(coWebsite);
- resolve();
+ 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 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");
- });
- });
- }));
- return this.currentOperationPromise;
- }
-
public getGameSize(): { width: number; height: number } {
- if (this.openedMain === iframeStates.closed) {
+ if (this.getMainState() === iframeStates.closed) {
return {
width: window.innerWidth,
height: window.innerHeight,
diff --git a/front/src/WebRtc/JitsiFactory.ts b/front/src/WebRtc/JitsiFactory.ts
index 8f7ed952..d328a72d 100644
--- a/front/src/WebRtc/JitsiFactory.ts
+++ b/front/src/WebRtc/JitsiFactory.ts
@@ -1,7 +1,8 @@
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 CancelablePromise from "cancelable-promise";
interface jitsiConfigInterface {
startWithAudioMuted: boolean;
@@ -134,122 +135,96 @@ 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) {
- await 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(iframe);
+ });
+
+ this.jitsiApi.addListener("readyToClose", () => {
+ this.closeOrUnload(iframe);
+ });
+
+ 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) {
- coWebsiteManager.closeCoWebsite(coWebsite).catch(() => {
- console.error("Error during closing a Jitsi Meet");
- });
+ private closeOrUnload = function (iframe: HTMLIFrameElement) {
+ const coWebsite = coWebsiteManager.getCoWebsites().find((coWebsite) => coWebsite.getIframe() === iframe);
+
+ if (!coWebsite) {
+ return;
+ }
+
+ if (coWebsite.isClosable()) {
+ coWebsiteManager.closeCoWebsite(coWebsite);
} else {
- coWebsiteManager.unloadCoWebsite(coWebsite).catch(() => {
- console.error("Error during unloading a Jitsi Meet");
+ coWebsiteManager.unloadCoWebsite(coWebsite).catch((err) => {
+ console.error("Cannot unload co-website from the Jitsi factory", err);
});
}
};
- public restart() {
- if (!this.jitsiApi) {
- return;
- }
-
- this.jitsiApi.addListener("audioMuteStatusChanged", this.audioCallback);
- 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();
- return;
- }
-
- this.jitsiApi.addListener("videoConferenceLeft", () => {
- this.closeOrUnload(coWebsite);
- });
-
- this.jitsiApi.addListener("readyToClose", () => {
- this.closeOrUnload(coWebsite);
- });
- }
-
public stop() {
if (!this.jitsiApi) {
return;
@@ -284,8 +259,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;
@@ -304,6 +279,10 @@ class JitsiFactory {
};
document.head.appendChild(jitsiScript);
+
+ cancel(() => {
+ jitsiScript.remove();
+ });
});
}
}
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/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..77acbb4a 100644
--- a/front/src/i18n/fr-FR/index.ts
+++ b/front/src/i18n/fr-FR/index.ts
@@ -12,9 +12,10 @@ 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,
+ ...(en_US as Translation),
language: "Français",
country: "France",
audio,
@@ -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 bf3ede7a..f8c58990 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;
diff --git a/maps/tests/DoorTest/map.json b/maps/tests/DoorTest/map.json
new file mode 100644
index 00000000..b954b97c
--- /dev/null
+++ b/maps/tests/DoorTest/map.json
@@ -0,0 +1,197 @@
+{ "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":[
+ {
+ "height":101.5,
+ "id":11,
+ "name":"",
+ "rotation":0,
+ "text":
+ {
+ "text":"You should be able to get here only if the path isn't blocked",
+ "wrap":true
+ },
+ "type":"",
+ "visible":true,
+ "width":383,
+ "x":81.226595249185,
+ "y":297.048206800186
+ },
+ {
+ "height":101.5,
+ "id":12,
+ "name":"",
+ "rotation":0,
+ "text":
+ {
+ "text":"Try to move to the next room by using right-click \/ tap movement. Click \"Toggle Door\" button to block \/ make access.",
+ "wrap":true
+ },
+ "type":"",
+ "visible":true,
+ "width":383,
+ "x":81,
+ "y":99.75
+ }],
+ "opacity":1,
+ "type":"objectgroup",
+ "visible":true,
+ "x":0,
+ "y":0
+ }],
+ "nextlayerid":9,
+ "nextobjectid":13,
+ "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 @@
+
+
+
+
+
+
+
+
+Toggle Door
+
+
+
diff --git a/maps/tests/index.html b/maps/tests/index.html
index 9d8fe146..e8bf4369 100644
--- a/maps/tests/index.html
+++ b/maps/tests/index.html
@@ -34,7 +34,7 @@
- Success Failure Pending
+ Success Failure Pending
Test parallax effect
@@ -48,6 +48,14 @@
Test animated tiles
+
+
+ Success Failure Pending
+
+
+ Test Doors
+
+
Success Failure Pending