From f3c4d344b3020f6bbfce0484201cac7a009f7988 Mon Sep 17 00:00:00 2001 From: GRL Date: Tue, 24 Aug 2021 17:35:06 +0200 Subject: [PATCH] Add iframe submenu by scripting API Delete menu by scripting API --- front/src/Api/Events/IframeEvent.ts | 4 +- front/src/Api/Events/SetVariableEvent.ts | 2 +- .../Api/Events/ui/MenuItemRegisterEvent.ts | 27 -- front/src/Api/Events/ui/MenuRegisterEvent.ts | 42 ++ front/src/Api/IframeListener.ts | 19 +- front/src/Api/iframe/ui.ts | 24 +- .../src/Components/Menu/CustomSubMenu.svelte | 16 + front/src/Components/Menu/Menu.svelte | 18 +- front/src/Stores/MenuStore.ts | 34 +- maps/tests/Metadata/customMenu.html | 32 +- maps/tests/Metadata/customMenu.json | 361 +++++------------- 11 files changed, 251 insertions(+), 328 deletions(-) delete mode 100644 front/src/Api/Events/ui/MenuItemRegisterEvent.ts create mode 100644 front/src/Api/Events/ui/MenuRegisterEvent.ts create mode 100644 front/src/Components/Menu/CustomSubMenu.svelte diff --git a/front/src/Api/Events/IframeEvent.ts b/front/src/Api/Events/IframeEvent.ts index ed723241..d561f9f7 100644 --- a/front/src/Api/Events/IframeEvent.ts +++ b/front/src/Api/Events/IframeEvent.ts @@ -15,7 +15,6 @@ import type { SetPropertyEvent } from "./setPropertyEvent"; import type { LoadSoundEvent } from "./LoadSoundEvent"; import type { PlaySoundEvent } from "./PlaySoundEvent"; import type { MenuItemClickedEvent } from "./ui/MenuItemClickedEvent"; -import type { MenuItemRegisterEvent } from "./ui/MenuItemRegisterEvent"; import type { HasPlayerMovedEvent } from "./HasPlayerMovedEvent"; import type { SetTilesEvent } from "./SetTilesEvent"; import type { SetVariableEvent } from "./SetVariableEvent"; @@ -33,6 +32,7 @@ import type { TriggerActionMessageEvent, } from "./ui/TriggerActionMessageEvent"; import { isMessageReferenceEvent, isTriggerActionMessageEvent } from "./ui/TriggerActionMessageEvent"; +import type { MenuIframeRegisterEvent, MenuItemRegisterEvent, UnregisterMenuEvent } from "./ui/MenuRegisterEvent"; export interface TypedMessageEvent extends MessageEvent { data: T; @@ -64,6 +64,8 @@ export type IframeEventMap = { getState: undefined; loadTileset: LoadTilesetEvent; registerMenuCommand: MenuItemRegisterEvent; + registerMenuIframe: MenuIframeRegisterEvent; + unregisterMenu: UnregisterMenuEvent; setTiles: SetTilesEvent; modifyEmbeddedWebsite: Partial; // Note: name should be compulsory in fact }; diff --git a/front/src/Api/Events/SetVariableEvent.ts b/front/src/Api/Events/SetVariableEvent.ts index 3b4e9c85..77740683 100644 --- a/front/src/Api/Events/SetVariableEvent.ts +++ b/front/src/Api/Events/SetVariableEvent.ts @@ -1,5 +1,5 @@ import * as tg from "generic-type-guard"; -import { isMenuItemRegisterEvent } from "./ui/MenuItemRegisterEvent"; +import { isMenuItemRegisterEvent } from "./ui/MenuRegisterEvent"; export const isSetVariableEvent = new tg.IsInterface() .withProperties({ diff --git a/front/src/Api/Events/ui/MenuItemRegisterEvent.ts b/front/src/Api/Events/ui/MenuItemRegisterEvent.ts deleted file mode 100644 index e9ee1a50..00000000 --- a/front/src/Api/Events/ui/MenuItemRegisterEvent.ts +++ /dev/null @@ -1,27 +0,0 @@ -import * as tg from "generic-type-guard"; -import { Subject } from "rxjs"; -import { subMenusStore } from "../../../Stores/MenuStore"; - -export const isMenuItemRegisterEvent = new tg.IsInterface() - .withProperties({ - menutItem: tg.isString, - }) - .get(); -/** - * A message sent from the iFrame to the game to add a new menu item. - */ -export type MenuItemRegisterEvent = tg.GuardedType; - -export const isMenuItemRegisterIframeEvent = new tg.IsInterface() - .withProperties({ - type: tg.isSingletonString("registerMenuCommand"), - data: isMenuItemRegisterEvent, - }) - .get(); - -const _registerMenuCommandStream: Subject = new Subject(); -export const registerMenuCommandStream = _registerMenuCommandStream.asObservable(); - -export function handleMenuItemRegistrationEvent(event: MenuItemRegisterEvent) { - subMenusStore.addMenu(event.menutItem); -} diff --git a/front/src/Api/Events/ui/MenuRegisterEvent.ts b/front/src/Api/Events/ui/MenuRegisterEvent.ts new file mode 100644 index 00000000..92bca580 --- /dev/null +++ b/front/src/Api/Events/ui/MenuRegisterEvent.ts @@ -0,0 +1,42 @@ +import * as tg from "generic-type-guard"; + +/** + * A message sent from a script to the game to add a new button in the menu. + */ +export const isMenuItemRegisterEvent = new tg.IsInterface() + .withProperties({ + menuItem: tg.isString, + }) + .get(); + +export type MenuItemRegisterEvent = tg.GuardedType; + +export const isMenuItemRegisterIframeEvent = new tg.IsInterface() + .withProperties({ + type: tg.isSingletonString("registerMenuCommand"), + data: isMenuItemRegisterEvent, + }) + .get(); + +/** + * A message sent from a script to the game to add an iframe submenu in the menu. + */ +export const isMenuIframeEvent = new tg.IsInterface() + .withProperties({ + name: tg.isString, + url: tg.isString, + }) + .get(); + +export type MenuIframeRegisterEvent = tg.GuardedType; + +/** + * A message sent from a script to the game to remove a custom menu from the menu + */ +export const isUnregisterMenuEvent = new tg.IsInterface() + .withProperties({ + name: tg.isString, + }) + .get(); + +export type UnregisterMenuEvent = tg.GuardedType; diff --git a/front/src/Api/IframeListener.ts b/front/src/Api/IframeListener.ts index 8ab68096..7a0fa9d1 100644 --- a/front/src/Api/IframeListener.ts +++ b/front/src/Api/IframeListener.ts @@ -29,12 +29,12 @@ import { isSetPropertyEvent, SetPropertyEvent } from "./Events/setPropertyEvent" import { isLayerEvent, LayerEvent } from "./Events/LayerEvent"; import type { HasPlayerMovedEvent } from "./Events/HasPlayerMovedEvent"; import { isLoadPageEvent } from "./Events/LoadPageEvent"; -import { handleMenuItemRegistrationEvent, isMenuItemRegisterIframeEvent } from "./Events/ui/MenuItemRegisterEvent"; +import { isMenuItemRegisterIframeEvent, isMenuIframeEvent, isUnregisterMenuEvent } from "./Events/ui/MenuRegisterEvent"; import { SetTilesEvent, isSetTilesEvent } from "./Events/SetTilesEvent"; import type { SetVariableEvent } from "./Events/SetVariableEvent"; import { ModifyEmbeddedWebsiteEvent, isEmbeddedWebsiteEvent } from "./Events/EmbeddedWebsiteEvent"; import { EmbeddedWebsite } from "./iframe/Room/EmbeddedWebsite"; -import { subMenusStore } from "../Stores/MenuStore"; +import { handleMenuRegistrationEvent, handleMenuUnregisterEvent } from "../Stores/MenuStore"; type AnswererCallback = ( query: IframeQueryMap[T]["query"], @@ -256,16 +256,25 @@ class IframeListener { } else if (payload.type == "onPlayerMove") { this.sendPlayerMove = true; } else if (isMenuItemRegisterIframeEvent(payload)) { - const data = payload.data.menutItem; + const data = payload.data.menuItem; // @ts-ignore this.iframeCloseCallbacks.get(iframe).push(() => { - subMenusStore.removeMenu(data); + handleMenuUnregisterEvent(data); }); - handleMenuItemRegistrationEvent(payload.data); + handleMenuRegistrationEvent(payload.data.menuItem); } else if (payload.type == "setTiles" && isSetTilesEvent(payload.data)) { this._setTilesStream.next(payload.data); } else if (payload.type == "modifyEmbeddedWebsite" && isEmbeddedWebsiteEvent(payload.data)) { this._modifyEmbeddedWebsiteStream.next(payload.data); + } else if (payload.type == "registerMenuIframe" && isMenuIframeEvent(payload.data)) { + const data = payload.data.name; + // @ts-ignore + this.iframeCloseCallbacks.get(iframe).push(() => { + handleMenuUnregisterEvent(data); + }); + handleMenuRegistrationEvent(payload.data.name, payload.data.url, foundSrc); + } else if (payload.type == "unregisterMenu" && isUnregisterMenuEvent(payload.data)) { + handleMenuUnregisterEvent(payload.data.name); } } }, diff --git a/front/src/Api/iframe/ui.ts b/front/src/Api/iframe/ui.ts index ab5b2007..e093835a 100644 --- a/front/src/Api/iframe/ui.ts +++ b/front/src/Api/iframe/ui.ts @@ -109,11 +109,33 @@ export class WorkAdventureUiCommands extends IframeApiContribution + export let iframe: string; + + + + + + + \ No newline at end of file diff --git a/front/src/Components/Menu/Menu.svelte b/front/src/Components/Menu/Menu.svelte index 28ae17eb..607eb66f 100644 --- a/front/src/Components/Menu/Menu.svelte +++ b/front/src/Components/Menu/Menu.svelte @@ -6,7 +6,8 @@ import AboutRoomSubMenu from "./AboutRoomSubMenu.svelte"; import GlobalMessageSubMenu from "./GlobalMessagesSubMenu.svelte"; import ContactSubMenu from "./ContactSubMenu.svelte"; - import {menuVisiblilityStore, SubMenusInterface, subMenusStore} from "../../Stores/MenuStore"; + import CustomSubMenu from "./CustomSubMenu.svelte" + import {customMenuIframe, menuVisiblilityStore, SubMenusInterface, subMenusStore} from "../../Stores/MenuStore"; import {userIsAdminStore} from "../../Stores/GameStore"; import {onMount} from "svelte"; import {get} from "svelte/store"; @@ -14,7 +15,8 @@ import {CONTACT_URL} from "../../Enum/EnvironmentVariable"; let activeSubMenu: string = SubMenusInterface.settings; - let activeComponent: typeof SettingsSubMenu = SettingsSubMenu; + let activeComponent: typeof SettingsSubMenu | typeof CustomSubMenu= SettingsSubMenu; + let props: {iframe: string}; onMount(() => { if(!get(userIsAdminStore)) { @@ -51,8 +53,14 @@ activeComponent = ContactSubMenu; break; default: - sendMenuClickedEvent(menu); - menuVisiblilityStore.set(false); + const customMenu = customMenuIframe.get(menu); + if (customMenu !== undefined) { + props = {iframe: customMenu}; + activeComponent = CustomSubMenu; + } else { + sendMenuClickedEvent(menu); + menuVisiblilityStore.set(false); + } break; } } else throw ("There is no menu called " + menu); @@ -84,7 +92,7 @@ diff --git a/front/src/Stores/MenuStore.ts b/front/src/Stores/MenuStore.ts index 8581f9cc..cf8094d3 100644 --- a/front/src/Stores/MenuStore.ts +++ b/front/src/Stores/MenuStore.ts @@ -1,4 +1,4 @@ -import { writable } from "svelte/store"; +import { get, writable } from "svelte/store"; import Timeout = NodeJS.Timeout; export const menuIconVisiblilityStore = writable(false); @@ -66,3 +66,35 @@ function createSubMenusStore() { } export const subMenusStore = createSubMenusStore(); + +export const customMenuIframe = new Map(); + +export function handleMenuRegistrationEvent( + menuName: string, + iframeUrl: string | undefined = undefined, + source: string | undefined = undefined +) { + if (get(subMenusStore).includes(menuName)) { + console.warn("The menu " + menuName + " already exist."); + return; + } + + subMenusStore.addMenu(menuName); + + if (iframeUrl !== undefined) { + //Add a subMenu from an iframe to the menu + const url = new URL(iframeUrl, source); + customMenuIframe.set(menuName, url.toString()); + } +} + +export function handleMenuUnregisterEvent(menuName: string) { + const subMenuGeneral: string[] = Object.values(SubMenusInterface); + if (subMenuGeneral.includes(menuName)) { + console.warn("The menu " + menuName + " is a mandatory menu. It can't be remove"); + return; + } + + subMenusStore.removeMenu(menuName); + customMenuIframe.delete(menuName); +} diff --git a/maps/tests/Metadata/customMenu.html b/maps/tests/Metadata/customMenu.html index 404673f3..098d8ab1 100644 --- a/maps/tests/Metadata/customMenu.html +++ b/maps/tests/Metadata/customMenu.html @@ -1,20 +1,18 @@ - - - - -

Add a custom menu

- + + + + +

Add a custom menu

+ \ No newline at end of file diff --git a/maps/tests/Metadata/customMenu.json b/maps/tests/Metadata/customMenu.json index 49840d0b..c107aaf2 100644 --- a/maps/tests/Metadata/customMenu.json +++ b/maps/tests/Metadata/customMenu.json @@ -1,279 +1,100 @@ { "compressionlevel":-1, - "editorsettings": + "height":10, + "infinite":false, + "layers":[ { - "export": - { - "target":"." - } + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":10, + "id":1, + "name":"start", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 }, - "height":10, - "infinite":false, - "layers":[ + { + "data":[33, 34, 34, 34, 34, 34, 35, 0, 0, 0, 41, 42, 42, 42, 42, 42, 43, 0, 0, 0, 41, 42, 42, 42, 42, 42, 43, 0, 0, 0, 41, 42, 42, 42, 42, 42, 59, 34, 34, 35, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 0, 0, 0, 42, 42, 42, 42, 42, 42, 43, 0, 0, 0, 42, 42, 42, 42, 42, 42, 43, 0, 0, 0, 50, 50, 50, 50, 50, 50, 51], + "height":10, + "id":2, + "name":"bottom", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 33, 34, 35, 0, 0, 0, 0, 0, 0, 0, 41, 42, 43, 0, 0, 0, 0, 0, 0, 0, 49, 50, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":10, + "id":3, + "name":"ZoneIframeMenu", + "opacity":1, + "properties":[ { - "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 52, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - "height":10, - "id":1, - "name":"start", - "opacity":1, - "type":"tilelayer", - "visible":true, - "width":10, - "x":0, - "y":0 - }, - { - "data":[33, 34, 34, 34, 34, 34, 34, 34, 34, 35, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 49, 50, 50, 50, 50, 50, 50, 50, 50, 51], - "height":10, - "id":2, - "name":"bottom", - "opacity":1, - "type":"tilelayer", - "visible":true, - "width":10, - "x":0, - "y":0 - }, - { - "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - "height":10, - "id":6, - "name":"exit", - "opacity":1, - "properties":[ - { - "name":"exitUrl", - "type":"string", - "value":"showHideLayer.json" - }], - "type":"tilelayer", - "visible":true, - "width":10, - "x":0, - "y":0 - }, - { - "data":[0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 128, 128, 128, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - "height":10, - "id":4, - "name":"metadata", - "opacity":1, - "properties":[ - { - "name":"openWebsite", - "type":"string", - "value":"customMenu.html" - }, - { - "name":"openWebsiteAllowApi", - "type":"bool", - "value":true - }], - "type":"tilelayer", - "visible":true, - "width":10, - "x":0, - "y":0 - }, - { - "draworder":"topdown", - "id":5, - "name":"floorLayer", - "objects":[ - { - "height":217.142414860681, - "id":1, - "name":"", - "rotation":0, - "text": - { - "fontfamily":"Sans Serif", - "pixelsize":9, - "text":"Test : \nWalk on the grass, an iframe open.\nResult : \nOpen the menu, a new sub-menu is displayed.\n\nTest : \nExit the grass\nResult : \nOpen the menu, the submenu has disappeared.\n\nTest : \nClick on the 'HELP' menu.\nResult : \nChat open and a 'HELP' message is displayed.\n\nTest : \nWalk on the red tile then open the menu.\nResult : \nYou have exit the room to another room, the submenu has disappeared.\n", - "wrap":true - }, - "type":"", - "visible":true, - "width":305.097705765524, - "x":15.1244925229277, - "y":103.029937496349 - }], - "opacity":1, - "type":"objectgroup", - "visible":true, - "x":0, - "y":0 + "name":"zone", + "type":"string", + "value":"iframeMenu" }], - "nextlayerid":7, - "nextobjectid":2, - "orientation":"orthogonal", - "renderorder":"right-down", - "tiledversion":"1.4.3", - "tileheight":32, - "tilesets":[ + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 33, 34, 35, 0, 0, 0, 0, 0, 0, 0, 41, 42, 43, 0, 0, 0, 0, 0, 0, 0, 49, 50, 51, 0, 0, 0, 0, 0, 0, 0], + "height":10, + "id":4, + "name":"CoWebSiteIframeMenu", + "opacity":1, + "properties":[ { - "columns":8, - "firstgid":1, - "image":"tileset_dungeon.png", - "imageheight":256, - "imagewidth":256, - "margin":0, - "name":"TDungeon", - "spacing":0, - "tilecount":64, - "tileheight":32, - "tiles":[ - { - "id":0, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":1, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":2, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":3, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":4, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":8, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":9, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":10, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":11, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":12, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":16, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":17, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":18, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":19, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }, - { - "id":20, - "properties":[ - { - "name":"collides", - "type":"bool", - "value":true - }] - }], - "tilewidth":32 - }, + "name":"openWebsite", + "type":"string", + "value":"customMenu.html" + }, { - "columns":8, - "firstgid":65, - "image":"floortileset.png", - "imageheight":288, - "imagewidth":256, - "margin":0, - "name":"Floor", - "spacing":0, - "tilecount":72, - "tileheight":32, - "tilewidth":32 + "name":"openWebsiteAllowApi", + "type":"bool", + "value":true }], - "tilewidth":32, - "type":"map", - "version":1.4, - "width":10 + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }], + "nextlayerid":5, + "nextobjectid":1, + "orientation":"orthogonal", + "properties":[ + { + "name":"script", + "type":"string", + "value":"customMenu.js" + }], + "renderorder":"right-down", + "tiledversion":"1.7.1", + "tileheight":32, + "tilesets":[ + { + "columns":8, + "firstgid":1, + "image":"tileset_dungeon.png", + "imageheight":256, + "imagewidth":256, + "margin":0, + "name":"tileset_dungeon", + "spacing":0, + "tilecount":64, + "tileheight":32, + "tilewidth":32 + }], + "tilewidth":32, + "type":"map", + "version":"1.6", + "width":10 } \ No newline at end of file