diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 8e97dc1..0e292fc 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -43,6 +43,7 @@ jobs: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} CI: true - run: sed -i 's|""|"https://unpkg.com/@workadventure/scripting-api-extra@${{ env.release_tag }}/dist"|g' src/Features/default_assets_url.ts + - run: sed -i 's|http://workadventure.localhost|https://workadventu.re|g' src/Features/default_api_url.ts - run: npm run build --if-present - run: npm run test - run: curl -s https://codecov.io/bash | bash diff --git a/docs/images/logo_upload_configuration_properties.png b/docs/images/logo_upload_configuration_properties.png new file mode 100644 index 0000000..35154f9 Binary files /dev/null and b/docs/images/logo_upload_configuration_properties.png differ diff --git a/docs/images/logo_upload_configuration_tile.png b/docs/images/logo_upload_configuration_tile.png new file mode 100644 index 0000000..74dea3d Binary files /dev/null and b/docs/images/logo_upload_configuration_tile.png differ diff --git a/docs/images/logo_upload_demo.gif b/docs/images/logo_upload_demo.gif new file mode 100644 index 0000000..33fdf9a Binary files /dev/null and b/docs/images/logo_upload_demo.gif differ diff --git a/docs/images/logo_upload_embedded_website_object.png b/docs/images/logo_upload_embedded_website_object.png new file mode 100644 index 0000000..2a93b12 Binary files /dev/null and b/docs/images/logo_upload_embedded_website_object.png differ diff --git a/docs/images/logo_upload_embedded_website_properties.png b/docs/images/logo_upload_embedded_website_properties.png new file mode 100644 index 0000000..66ed8a7 Binary files /dev/null and b/docs/images/logo_upload_embedded_website_properties.png differ diff --git a/docs/images/logo_upload_layers.png b/docs/images/logo_upload_layers.png new file mode 100644 index 0000000..9377cab Binary files /dev/null and b/docs/images/logo_upload_layers.png differ diff --git a/docs/images/logo_upload_objects.png b/docs/images/logo_upload_objects.png new file mode 100644 index 0000000..52d13d2 Binary files /dev/null and b/docs/images/logo_upload_objects.png differ diff --git a/docs/images/logo_upload_variable_object.png b/docs/images/logo_upload_variable_object.png new file mode 100644 index 0000000..8c381a6 Binary files /dev/null and b/docs/images/logo_upload_variable_object.png differ diff --git a/docs/images/logo_upload_variable_properties.png b/docs/images/logo_upload_variable_properties.png new file mode 100644 index 0000000..33da7bd Binary files /dev/null and b/docs/images/logo_upload_variable_properties.png differ diff --git a/docs/logo-upload.md b/docs/logo-upload.md new file mode 100644 index 0000000..30dd0b4 --- /dev/null +++ b/docs/logo-upload.md @@ -0,0 +1,111 @@ +{.section-title.accent.text-primary} +# Logo upload + +{.alert.alert-info} +**Important!** To use the "logo upload" feature, you need to [import the "Scripting API Extra" script in your map](about.md#importing-the-extended-features) + +The logo upload feature let you customize your maps in a simple way. +By defining some objects and properties in Tiled you can have a logo appearing in your map. Combined with the local configuration feature, +you will have the possibility to change your logo at any time just be heading in front of him and by uploading your brand-new logo file! + +To achieve this you will need to define 1 layer, 1 object layer and 2 objects in Tiled. + +
+ +
Logo upload demo
+
+ +Let's see how this can be done! + +## The variable + +{.alert.alert-info} +You can read more about variables [here](https://workadventu.re/map-building/variables.md). + +First things first, let's create the variable that will hold the logo URL of the uploaded file. +Fo this to happen, you need first to upload an image, right? + +That's why when creating the variable you will pass a `type: "upload"` to its properties, this will have the effect of displaying an upload button in the configuration panel. + +For the upload input to appear in the configuration panel you MUST move this variable object inside an object layer called `configuration`. + +
+ +
Variable object
+
+ +
+ +
Variable properties
+
+ +As you can see, besides defining the new `upload` type we can also pass optional properties that will restrict the width and height of you image. +Specify the number of pixels in width as `imageWidth` value and the number of pixels in height as `imageHeight` value. + +## The embedded website + +{.alert.alert-info} +You can read more about embedded websites [here](https://workadventu.re/map-building/website-in-map.md). + +
+ +
Embedded website object
+
+ +Create a rectangle object outside the `configuration` object layer (in `floorLayer` for example), this will be out iFrame zone that will be in charge of displaying the actual image. +You have to set its properties to allow the iFrame to interact with external programs (`allowApi: true`) and set the `url` to use the value of the previously defined variable. + +
+ +
Embedded website properties
+
+ +Now, what we are trying to say with this mustache syntax *(it complicates things a bit, but it's actually simple)* is "If there is a URL inside the variable 'logoUrl', pick this URL, otherwise just display another default image": `{{#logoUrl}}{{{logoUrl}}}{{/logoUrl}}{{^logoUrl}}https://workadventu.re/favicon-32x32.png{{/logoUrl}}`. + +## The configuration layer + +{.alert.alert-info} +You can read more about the local configuration layer [here](https://workadventu.re/map-building-extra/automatic-configuration.md#local-configuration-panel). + +We have the variable object that will hold the image URL, we have the zone to display it, now the final step is to define a zone to be able to upload some new images by configuring the variable value. + +We just draw a single tile in front of the logo in a dedicated layer. In its properties we specify the name of the variable to configure with `openConfig: logoUrl`. + +
+ +
Configuration tile
+
+ +{.alert.alert-info} +The rest of the properties are not mandatory. You can restrict who can access to this configuration as well. + +
+ +
Configuration properties
+
+ +## Let's recap + +To recap, we created 2 objects. One must be in the `configuration` object layer in order to be configurable. + +
+ +
Map objects
+
+ +Then, apart from the layers we used to create for a map, we created a layer that will have one tile. This will be the zone in which you can walk to open the local configuration panel. We named this layer 'logoConfiguration'. + +
+ +
Map layers
+
+ +That's it! This feature will be integrated with all of our preset maps. + +
+ +
Logo upload demo
+
+ +If you want to integrate it yourself into your awesome custom map, now you can! +You can find the full code of the map that tests the logo upload feature [here](https://github.com/workadventure/scripting-api-extra/blob/main/test/maps/configuration_logo.json). diff --git a/docs/menu.php b/docs/menu.php index 6d3bc8b..b8b4789 100644 --- a/docs/menu.php +++ b/docs/menu.php @@ -38,6 +38,11 @@ 'url' => '/map-building-extra/automatic-configuration.md', 'markdown' => 'scripting_api_extra_doc.automatic-configuration', 'editUrl' => 'https://github.com/workadventure/scripting-api-extra/edit/main/docs/automatic-configuration.md', + ],[ + 'title' => 'Logo upload', + 'url' => '/map-building-extra/logo-upload.md', + 'markdown' => 'scripting_api_extra_doc.logo-upload', + 'editUrl' => 'https://github.com/workadventure/scripting-api-extra/edit/main/docs/logo-upload.md', ],[ 'title' => 'Properties reference', 'url' => '/map-building-extra/reference.md', diff --git a/package-lock.json b/package-lock.json index 01ce39b..4a4a5d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2157,9 +2157,9 @@ "dev": true }, "@workadventure/iframe-api-typings": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@workadventure/iframe-api-typings/-/iframe-api-typings-1.4.14.tgz", - "integrity": "sha512-p9uoU1m6aZD7ut+TGorMRaSB9oxDblwhOYZQgPD2U55jVNmSI9ZAlpp+J4hAvUaIOSP71G2qB7M/FoeyCyX4yA==", + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/@workadventure/iframe-api-typings/-/iframe-api-typings-1.6.4.tgz", + "integrity": "sha512-mz4jvsu/Gn6IuIY84+wYHxT53mf+PY5N/OcpxcE5R/YqxTGbWOW5dFywlDpjG8QWOzunVkYwDCCAPercihJRUg==", "dev": true }, "@workadventure/tiled-map-type-guard": { diff --git a/package.json b/package.json index db3287b..5a51d1c 100644 --- a/package.json +++ b/package.json @@ -60,7 +60,7 @@ "@types/mini-css-extract-plugin": "^2.2.0", "@types/mustache": "^4.1.2", "@types/webpack-dev-server": "^4.1.0", - "@workadventure/iframe-api-typings": "^1.4.14", + "@workadventure/iframe-api-typings": "^1.6.4", "copy-webpack-plugin": "^9.0.1", "cross-env": "^7.0.3", "css-loader": "^5.2.4", diff --git a/src/Features/configuration.ts b/src/Features/configuration.ts index d087895..e91e1a4 100644 --- a/src/Features/configuration.ts +++ b/src/Features/configuration.ts @@ -31,20 +31,15 @@ export async function initConfiguration(assetsUrl?: string | undefined): Promise const properties = new Properties(layer.properties); const openConfigVariables = properties.getString("openConfig"); if (openConfigVariables && layer.type === "tilelayer") { - initLocalConfigurationPanel(openConfigVariables, properties); + initLocalConfigurationPanel(layer.name, openConfigVariables, properties); } } } } -function initLocalConfigurationPanel(openConfigVariables: string, properties: Properties): void { +function initLocalConfigurationPanel(layerName: string, openConfigVariables: string, properties: Properties): void { let actionMessage: ActionMessage | undefined = undefined; - const zoneName = properties.getString("zone"); - if (!zoneName) { - throw new Error('Missing "zone" property'); - } - const tag = properties.getString("openConfigAdminTag"); let allowedByTag = true; if (tag && !WA.player.tags.includes(tag)) { @@ -67,7 +62,7 @@ function initLocalConfigurationPanel(openConfigVariables: string, properties: Pr WA.nav.closeCoWebSite(); } - WA.room.onEnterZone(zoneName, () => { + WA.room.onEnterLayer(layerName).subscribe(() => { const openConfigTriggerValue = properties.getString("openConfigTrigger"); // Do not display conf panel if the user is not allowed by tag @@ -80,7 +75,7 @@ function initLocalConfigurationPanel(openConfigVariables: string, properties: Pr } }); - WA.room.onLeaveZone(zoneName, () => { + WA.room.onLeaveLayer(layerName).subscribe(() => { if (actionMessage) { actionMessage.remove(); closeConfigurationPanel(); diff --git a/src/Features/default_api_url.ts b/src/Features/default_api_url.ts new file mode 100644 index 0000000..6c9c83f --- /dev/null +++ b/src/Features/default_api_url.ts @@ -0,0 +1,5 @@ +/** + * The base URL for the default API. + * This file is updated dynamically during the package build process. + */ +export const defaultApiUrl = "http://workadventure.localhost/api"; diff --git a/src/Features/properties_templates.ts b/src/Features/properties_templates.ts index 12d0726..b88b86e 100644 --- a/src/Features/properties_templates.ts +++ b/src/Features/properties_templates.ts @@ -15,25 +15,51 @@ export async function initPropertiesTemplates(): Promise { ) { continue; } + const template = new TemplateValue(property.value, WA.state); if (template.isPureString()) { continue; } const newValue = template.getValue(); - setProperty(layerName, property.name, newValue); + setLayerProperty(layerName, property.name, newValue); template.onChange((newValue) => { - setProperty(layerName, property.name, newValue); + setLayerProperty(layerName, property.name, newValue); }); } + + // Parse the URL of the integrated websites (for example if mustache is used) + // Here we want to select the Tiled object layers with the type 'website' and the property 'url' + const promises = []; + if (layer.type === "objectgroup") { + for (const object of layer.objects) { + if (object.type === "website") { + for (const property of object.properties) { + if (property.name === "url") { + const template = new TemplateValue(property.value, WA.state); + if (template.isPureString()) { + continue; + } + const newValue = template.getValue(); + promises.push(setWebsiteProperty(object.name, newValue)); + + template.onChange((newValue) => { + setWebsiteProperty(object.name, newValue); + }); + } + } + } + } + } + await Promise.all(promises); } } /** - * Sets the property value on the map. + * Sets the property value of a layer on the map. * Furthermore, if the property name is "visible", modify the visibility of the layer. */ -function setProperty(layerName: string, propertyName: string, value: string): void { +function setLayerProperty(layerName: string, propertyName: string, value: string): void { WA.room.setProperty(layerName, propertyName, value); if (propertyName === "visible") { if (value) { @@ -43,3 +69,11 @@ function setProperty(layerName: string, propertyName: string, value: string): vo } } } + +/** + * Sets the property value of an object of type 'website' on the map. + */ +async function setWebsiteProperty(objectName: string, value: string): Promise { + const website = await WA.room.website.get(objectName); + website.url = value; +} diff --git a/src/Iframes/Configuration/Components/Field.svelte b/src/Iframes/Configuration/Components/Field.svelte index 24401af..f932439 100644 --- a/src/Iframes/Configuration/Components/Field.svelte +++ b/src/Iframes/Configuration/Components/Field.svelte @@ -1,7 +1,9 @@ @@ -49,6 +62,30 @@ {/each} +{:else if type === 'upload' } +
+ {label} +
+ + +
+
+ {#if $formStore.showImage} + Preview + {:else} + / + {/if} +
+ + +
+
+
{:else}
@@ -59,6 +96,9 @@
{ description }
{/if} +{#if $formStore.error } +

{ $formStore.error }

+{/if} diff --git a/src/Iframes/Configuration/Stores/form.ts b/src/Iframes/Configuration/Stores/form.ts new file mode 100644 index 0000000..c9bd71b --- /dev/null +++ b/src/Iframes/Configuration/Stores/form.ts @@ -0,0 +1,77 @@ +import { writable } from "svelte/store"; + +interface Form { + error: string; + image: HTMLImageElement; + showImage: boolean; + formData: FormData; +} + +const defaultFormState: Form = { + error: "", + image: new Image(0, 0), + showImage: false, + formData: new FormData(), +}; + +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type +function createFormAction() { + const { subscribe, set, update } = writable
(defaultFormState); + + return { + subscribe, + setError: (error: string): void => { + update((form: Form) => { + form.error = error; + return form; + }); + }, + clearError: (): void => { + update((form: Form) => { + form.error = ""; + return form; + }); + }, + setImagePreviewMaxWidth: (width: string): void => { + update((form: Form) => { + form.image.style.maxWidth = "" + width + "px"; + return form; + }); + }, + setImagePreviewMaxHeight: (height: string): void => { + update((form: Form) => { + form.image.style.maxHeight = "" + height + "px"; + return form; + }); + }, + setImageSource: (source: string): void => { + update((form: Form) => { + form.image.setAttribute("src", source); + return form; + }); + }, + changeImageVisibility: (mustShowImage: boolean): void => { + update((form: Form) => { + form.showImage = mustShowImage; + return form; + }); + }, + setFormDataFile: (file: File): void => { + update((form: Form) => { + form.formData.append("file", file); + return form; + }); + }, + setFormDataProperty: (propertyName: string, propertyValue: string): void => { + update((form: Form) => { + form.formData.set(propertyName, propertyValue); + return form; + }); + }, + clearForm: (): void => { + set(defaultFormState); + }, + }; +} + +export const formStore = createFormAction(); diff --git a/src/Uploader.ts b/src/Uploader.ts new file mode 100644 index 0000000..ae66c6b --- /dev/null +++ b/src/Uploader.ts @@ -0,0 +1,72 @@ +// eslint-disable-next-line @typescript-eslint/triple-slash-reference +/// + +import { defaultApiUrl } from "./Features/default_api_url"; +import { formStore } from "./Iframes/Configuration/Stores/form"; +import type { VariableDescriptor } from "./VariablesExtra"; + +let formData: FormData; +const unsubscribe = formStore.subscribe((form) => { + formData = form.formData; +}); + +/** + * Builds the image preview and the form data + */ +export function prepareUpload(event: Event, variable: VariableDescriptor): void { + const files = (event.target as HTMLInputElement).files; + const file = files ? files[0] : null; + formStore.clearError(); + + if (file) { + formStore.changeImageVisibility(true); + const reader = new FileReader(); + reader.onload = () => { + if (typeof reader.result === "string") { + formStore.setFormDataFile(file); + formStore.setFormDataProperty("identifier", variable.name); + if (variable.properties.getString("imageWidth")) { + const width = variable.properties.mustGetString("imageWidth"); + // Just for the rendering, doesn't resize the actual file + formStore.setImagePreviewMaxWidth(width); + // This will resize the file, on server side + formStore.setFormDataProperty("imageWidth", width); + } + if (variable.properties.getString("imageHeight")) { + const height = variable.properties.mustGetString("imageHeight"); + // Just for the rendering, doesn't resize the actual file + formStore.setImagePreviewMaxHeight(height); + // This will resize the file, on server side + formStore.setFormDataProperty("imageHeight", height); + } + formStore.setImageSource(reader.result); + } + }; + reader.readAsDataURL(file); + return; + } + formStore.changeImageVisibility(false); +} + +/** + * Performs the upload and update the url variable + */ +export async function uploadFile(): Promise { + const token = WA.player.userRoomToken; + formStore.clearError(); + //!\ if you are self-hosting WA, this API must be added by your side + const response = await fetch(defaultApiUrl + "/upload-file", { + method: "POST", + body: formData, + headers: { + Authorization: `Bearer ${token}`, + }, + }).catch((e) => { + formStore.setError("Upload error. Please contact the support."); + throw e; + }); + + unsubscribe(); + const data = await response.json(); + return data.url; +} diff --git a/test/maps/configuration_logo.json b/test/maps/configuration_logo.json new file mode 100644 index 0000000..95fd711 --- /dev/null +++ b/test/maps/configuration_logo.json @@ -0,0 +1,1187 @@ +{ "compressionlevel":-1, + "height":12, + "infinite":false, + "layers":[ + { + "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, 555, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":12, + "id":2, + "name":"start", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":12, + "x":0, + "y":0 + }, + { + "data":[554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 554, 554, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 554, 554, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 554, 554, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 554, 554, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 554, 554, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 554, 554, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 554, 554, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554, 554], + "height":12, + "id":22, + "name":"collisions", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":12, + "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, 563, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":12, + "id":16, + "name":"logoConfiguration", + "opacity":1, + "properties":[ + { + "name":"openConfig", + "type":"string", + "value":"logoUrl" + }, + { + "name":"openConfigTrigger", + "type":"string", + "value":"onaction" + }, + { + "name":"openConfigTriggerMessage", + "type":"string", + "value":"Edit your logo" + }], + "type":"tilelayer", + "visible":true, + "width":12, + "x":0, + "y":0 + }, + { + "data":[23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 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":12, + "id":1, + "name":"floor", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":12, + "x":0, + "y":0 + }, + { + "data":[490, 501, 501, 501, 501, 501, 501, 501, 501, 501, 501, 491, 486, 532, 532, 532, 532, 532, 532, 532, 532, 532, 532, 486, 486, 544, 544, 544, 544, 544, 544, 544, 544, 544, 544, 486, 486, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 486, 486, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 486, 486, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 486, 486, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 486, 486, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 486, 486, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 486, 486, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 486, 486, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 486, 502, 500, 500, 500, 500, 500, 500, 500, 500, 500, 500, 503], + "height":12, + "id":8, + "name":"walls", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":12, + "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], + "height":12, + "id":18, + "name":"furniture", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":12, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":3, + "name":"floorLayer", + "objects":[ + { + "height":110.319857312723, + "id":17, + "name":"readme", + "rotation":0, + "text": + { + "color":"#ffffff", + "fontfamily":"Sans Serif", + "pixelsize":13, + "text":"Test:\n1. Open the configuration in front of the logo\n2. Upload your new logo\n\nResult:\nThe corresponding logo should appear", + "wrap":true + }, + "type":"", + "visible":true, + "width":293.790632697714, + "x":46.1288751169901, + "y":221.681301258499 + }, + { + "height":36.1595607503847, + "id":29, + "name":"logo", + "properties":[ + { + "name":"allowApi", + "type":"bool", + "value":true + }, + { + "name":"url", + "type":"string", + "value":"{{#logoUrl}}{{{logoUrl}}}{{\/logoUrl}}{{^logoUrl}}https:\/\/workadventu.re\/favicon-32x32.png{{\/logoUrl}}" + }], + "rotation":0, + "type":"website", + "visible":true, + "width":64.0286177779626, + "x":160, + "y":42.3989018759619 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":32, + "name":"configuration", + "objects":[ + { + "height":0, + "id":28, + "name":"logoUrl", + "point":true, + "properties":[ + { + "name":"default", + "type":"string", + "value":"" + }, + { + "name":"imageHeight", + "type":"string", + "value":"32" + }, + { + "name":"imageWidth", + "type":"string", + "value":"32" + }, + { + "name":"label", + "type":"string", + "value":"Select your logo" + }, + { + "name":"persist", + "type":"bool", + "value":true + }, + { + "name":"type", + "type":"string", + "value":"upload" + }], + "rotation":0, + "type":"variable", + "visible":true, + "width":0, + "x":127.247451482026, + "y":63.3014172680968 + }], + "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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":12, + "id":23, + "name":"abovePlayer", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":12, + "x":0, + "y":0 + }], + "nextlayerid":35, + "nextobjectid":34, + "orientation":"orthogonal", + "properties":[ + { + "name":"script", + "type":"string", + "value":"\/bundle.js" + }], + "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 + }, + { + "columns":16, + "firstgid":122, + "image":"walls.png", + "imageheight":480, + "imagewidth":512, + "margin":0, + "name":"walls", + "spacing":0, + "tilecount":240, + "tileheight":32, + "tiles":[ + { + "id":128, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":129, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":130, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":131, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":132, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":133, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":134, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":135, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":144, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":145, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":146, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":147, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":148, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":149, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":150, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":151, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":160, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":161, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":162, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":163, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":164, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":165, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":166, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":167, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":168, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":169, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":170, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":171, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":172, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":173, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":176, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":177, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":178, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":179, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":180, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":181, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":182, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":183, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":184, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":185, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":186, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":187, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":188, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":189, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":192, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":193, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":194, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":195, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":196, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":197, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":198, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":199, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":200, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":201, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":202, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":203, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":204, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":205, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":208, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":209, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":210, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":211, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":212, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":213, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":214, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":215, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":216, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":217, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":218, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":219, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":220, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":221, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":224, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":225, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":226, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":227, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":228, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":229, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":230, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":231, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":232, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":233, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":234, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":235, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":236, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":237, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }], + "tilewidth":32 + }, + { + "columns":6, + "firstgid":362, + "image":"Door2_pipo.png", + "imageheight":384, + "imagewidth":192, + "margin":0, + "name":"Door2_pipo", + "spacing":0, + "tilecount":72, + "tileheight":32, + "tiles":[ + { + "id":6, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":9, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":12, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":15, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":24, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":27, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":30, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":33, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":42, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":48, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":60, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":66, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }], + "tilewidth":32 + }, + { + "columns":12, + "firstgid":434, + "image":"tileset2.png", + "imageheight":320, + "imagewidth":384, + "margin":0, + "name":"tileset2", + "spacing":0, + "tilecount":120, + "tileheight":32, + "tilewidth":32 + }, + { + "columns":6, + "firstgid":554, + "image":"special-zones.png", + "imageheight":64, + "imagewidth":192, + "margin":0, + "name":"special-zones", + "spacing":0, + "tilecount":12, + "tileheight":32, + "tiles":[ + { + "id":0, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }], + "tilewidth":32 + }], + "tilewidth":32, + "type":"map", + "version":"1.6", + "width":12 +} \ No newline at end of file