From 0b08d9251ed4b446bee719d4ad9ee4dec7523ac3 Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Thu, 14 Oct 2021 16:17:50 +0200 Subject: [PATCH 1/5] Fix link of cowebsite propeerty test map fix --- maps/tests/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maps/tests/index.html b/maps/tests/index.html index 899066ef..578e8ea6 100644 --- a/maps/tests/index.html +++ b/maps/tests/index.html @@ -243,7 +243,7 @@ Success Failure Pending - Open co-websites by map property + Open co-websites by map property From 934e24f8377ea2d862b7d23ce95f5333f9c18cc9 Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Fri, 29 Oct 2021 16:44:51 +0200 Subject: [PATCH 2/5] Implement on enter/leave layer events --- docs/maps/api-deprecated.md | 1 + docs/maps/api-player.md | 4 +- front/src/Api/Events/ChangeLayerEvent.ts | 11 +++ front/src/Api/Events/HasPlayerMovedEvent.ts | 2 + front/src/Api/Events/IframeEvent.ts | 3 + front/src/Api/IframeListener.ts | 19 +++++ front/src/Api/iframe/room.ts | 50 ++++++++++++ front/src/Phaser/Game/GameMap.ts | 81 +++++++++++++++++-- front/src/Phaser/Game/GameScene.ts | 22 ++++- front/src/Phaser/Game/PlayerMovement.ts | 2 + front/src/Phaser/Player/Player.ts | 6 +- front/src/Stores/LayoutManagerStore.ts | 2 + front/tests/Phaser/Game/PlayerMovementTest.ts | 31 ++++++- pusher/src/Services/SocketManager.ts | 6 +- 14 files changed, 222 insertions(+), 18 deletions(-) create mode 100644 front/src/Api/Events/ChangeLayerEvent.ts diff --git a/docs/maps/api-deprecated.md b/docs/maps/api-deprecated.md index f2b582a5..cc96e12a 100644 --- a/docs/maps/api-deprecated.md +++ b/docs/maps/api-deprecated.md @@ -14,6 +14,7 @@ The list of functions below is **deprecated**. You should not use those but. use - Method `WA.goToRoom` is deprecated. It has been renamed to `WA.nav.goToRoom`. - Method `WA.openCoWebSite` is deprecated. It has been renamed to `WA.nav.openCoWebSite`. - Method `WA.closeCoWebSite` is deprecated. It has been renamed to `WA.nav.closeCoWebSite`. +- Method `WA.closeCoWebsite` is deprecated. It has been renamed to `WA.nav.closeCoWebsite`. - Method `WA.openPopup` is deprecated. It has been renamed to `WA.ui.openPopup`. - Method `WA.onChatMessage` is deprecated. It has been renamed to `WA.chat.onChatMessage`. - Method `WA.onEnterZone` is deprecated. It has been renamed to `WA.room.onEnterZone`. diff --git a/docs/maps/api-player.md b/docs/maps/api-player.md index ed73c32d..39a13d9e 100644 --- a/docs/maps/api-player.md +++ b/docs/maps/api-player.md @@ -68,7 +68,9 @@ The event has the following attributes : * **moving (boolean):** **true** when the current player is moving, **false** otherwise. * **direction (string):** **"right"** | **"left"** | **"down"** | **"top"** the direction where the current player is moving. * **x (number):** coordinate X of the current player. -* **y (number):** coordinate Y of the current player. +* **y (number):** coordinate Y of the current player. +* **oldX (number):** old coordinate X of the current player. +* **oldY (number):** old coordinate Y of the current player. **callback:** the function that will be called when the current player is moving. It contains the event. diff --git a/front/src/Api/Events/ChangeLayerEvent.ts b/front/src/Api/Events/ChangeLayerEvent.ts new file mode 100644 index 00000000..77ff8ede --- /dev/null +++ b/front/src/Api/Events/ChangeLayerEvent.ts @@ -0,0 +1,11 @@ +import * as tg from "generic-type-guard"; + +export const isChangeLayerEvent = new tg.IsInterface() + .withProperties({ + name: tg.isString, + }) + .get(); +/** + * A message sent from the game to the iFrame when a user enters or leaves a layer. + */ +export type ChangeLayerEvent = tg.GuardedType; diff --git a/front/src/Api/Events/HasPlayerMovedEvent.ts b/front/src/Api/Events/HasPlayerMovedEvent.ts index 87b45482..a3f1aa21 100644 --- a/front/src/Api/Events/HasPlayerMovedEvent.ts +++ b/front/src/Api/Events/HasPlayerMovedEvent.ts @@ -6,6 +6,8 @@ export const isHasPlayerMovedEvent = new tg.IsInterface() moving: tg.isBoolean, x: tg.isNumber, y: tg.isNumber, + oldX: tg.isOptional(tg.isNumber), + oldY: tg.isOptional(tg.isNumber), }) .get(); diff --git a/front/src/Api/Events/IframeEvent.ts b/front/src/Api/Events/IframeEvent.ts index 9e31b46c..abb492c5 100644 --- a/front/src/Api/Events/IframeEvent.ts +++ b/front/src/Api/Events/IframeEvent.ts @@ -29,6 +29,7 @@ import type { } from "./ui/TriggerActionMessageEvent"; import { isMessageReferenceEvent, isTriggerActionMessageEvent } from "./ui/TriggerActionMessageEvent"; import type { MenuRegisterEvent, UnregisterMenuEvent } from "./ui/MenuRegisterEvent"; +import type { ChangeLayerEvent } from "./ChangeLayerEvent"; export interface TypedMessageEvent extends MessageEvent { data: T; @@ -75,6 +76,8 @@ export interface IframeResponseEventMap { userInputChat: UserInputChatEvent; enterEvent: EnterLeaveEvent; leaveEvent: EnterLeaveEvent; + enterLayerEvent: ChangeLayerEvent; + leaveLayerEvent: ChangeLayerEvent; buttonClickedEvent: ButtonClickedEvent; hasPlayerMoved: HasPlayerMovedEvent; menuItemClicked: MenuItemClickedEvent; diff --git a/front/src/Api/IframeListener.ts b/front/src/Api/IframeListener.ts index caa59420..d626fc05 100644 --- a/front/src/Api/IframeListener.ts +++ b/front/src/Api/IframeListener.ts @@ -30,6 +30,7 @@ import { SetTilesEvent, isSetTilesEvent } from "./Events/SetTilesEvent"; import type { SetVariableEvent } from "./Events/SetVariableEvent"; import { ModifyEmbeddedWebsiteEvent, isEmbeddedWebsiteEvent } from "./Events/EmbeddedWebsiteEvent"; import { handleMenuRegistrationEvent, handleMenuUnregisterEvent } from "../Stores/MenuStore"; +import type { ChangeLayerEvent } from "./Events/ChangeLayerEvent"; type AnswererCallback = ( query: IframeQueryMap[T]["query"], @@ -395,6 +396,24 @@ class IframeListener { }); } + sendEnterLayerEvent(layerName: string) { + this.postMessage({ + type: "enterLayerEvent", + data: { + name: layerName, + } as ChangeLayerEvent, + }); + } + + sendLeaveLayerEvent(layerName: string) { + this.postMessage({ + type: "leaveLayerEvent", + data: { + name: layerName, + } as ChangeLayerEvent, + }); + } + hasPlayerMoved(event: HasPlayerMovedEvent) { if (this.sendPlayerMove) { this.postMessage({ diff --git a/front/src/Api/iframe/room.ts b/front/src/Api/iframe/room.ts index 22df49c9..cfa02807 100644 --- a/front/src/Api/iframe/room.ts +++ b/front/src/Api/iframe/room.ts @@ -1,6 +1,7 @@ import { Subject } from "rxjs"; import { EnterLeaveEvent, isEnterLeaveEvent } from "../Events/EnterLeaveEvent"; +import { ChangeLayerEvent, isChangeLayerEvent } from "../Events/ChangeLayerEvent"; import { IframeApiContribution, queryWorkadventure, sendToWorkadventure } from "./IframeApiContribution"; import { apiCallback } from "./registeredCallbacks"; @@ -12,6 +13,9 @@ import website from "./website"; const enterStreams: Map> = new Map>(); const leaveStreams: Map> = new Map>(); +const enterLayerStreams: Map> = new Map>(); +const leaveLayerStreams: Map> = new Map>(); + interface TileDescriptor { x: number; y: number; @@ -47,8 +51,25 @@ export class WorkadventureRoomCommands extends IframeApiContribution { + enterLayerStreams.get(payloadData.name)?.next(); + }, + }), + apiCallback({ + type: "leaveLayerEvent", + typeChecker: isChangeLayerEvent, + callback: (payloadData) => { + leaveLayerStreams.get(payloadData.name)?.next(); + }, + }), ]; + /** + * @deprecated Use onEnterLayer instead + */ onEnterZone(name: string, callback: () => void): void { let subject = enterStreams.get(name); if (subject === undefined) { @@ -57,6 +78,10 @@ export class WorkadventureRoomCommands extends IframeApiContribution void): void { let subject = leaveStreams.get(name); if (subject === undefined) { @@ -65,12 +90,35 @@ export class WorkadventureRoomCommands extends IframeApiContribution { + let subject = enterLayerStreams.get(layerName); + if (subject === undefined) { + subject = new Subject(); + enterLayerStreams.set(layerName, subject); + } + + return subject; + } + + onLeaveLayer(layerName: string): Subject { + let subject = leaveLayerStreams.get(layerName); + if (subject === undefined) { + subject = new Subject(); + leaveLayerStreams.set(layerName, subject); + } + + return subject; + } + showLayer(layerName: string): void { sendToWorkadventure({ type: "showLayer", data: { name: layerName } }); } + hideLayer(layerName: string): void { sendToWorkadventure({ type: "hideLayer", data: { name: layerName } }); } + setProperty(layerName: string, propertyName: string, propertyValue: string | number | boolean | undefined): void { sendToWorkadventure({ type: "setProperty", @@ -81,10 +129,12 @@ export class WorkadventureRoomCommands extends IframeApiContribution { const event = await queryWorkadventure({ type: "getMapData", data: undefined }); return event.data as ITiledMap; } + setTiles(tiles: TileDescriptor[]) { sendToWorkadventure({ type: "setTiles", diff --git a/front/src/Phaser/Game/GameMap.ts b/front/src/Phaser/Game/GameMap.ts index 0360859b..cd11c179 100644 --- a/front/src/Phaser/Game/GameMap.ts +++ b/front/src/Phaser/Game/GameMap.ts @@ -2,6 +2,7 @@ import type { ITiledMap, ITiledMapLayer, ITiledMapProperty } from "../Map/ITiled import { flattenGroupLayersMap } from "../Map/LayersFlattener"; import TilemapLayer = Phaser.Tilemaps.TilemapLayer; import { DEPTH_OVERLAY_INDEX } from "./DepthIndexes"; +import { iframeListener } from "../../Api/IframeListener"; export type PropertyChangeCallback = ( newValue: string | number | boolean | undefined, @@ -9,14 +10,25 @@ export type PropertyChangeCallback = ( allProps: Map ) => void; +export type layerChangeCallback = ( + layersChangedByAction: Array, + allLayersOnNewPosition: Array, + +) => void; + /** * A wrapper around a ITiledMap interface to provide additional capabilities. * It is used to handle layer properties. */ export class GameMap { + // oldKey is the index of the previous tile. + private oldKey: number | undefined; + // key is the index of the current tile. private key: number | undefined; private lastProperties = new Map(); - private callbacks = new Map>(); + private propertiesChangeCallbacks = new Map>(); + private enterLayerCallbacks = Array(); + private leaveLayerCallbacks = Array(); private tileNameMap = new Map(); private tileSetPropertyMap: { [tile_index: number]: Array } = {}; @@ -68,22 +80,32 @@ export class GameMap { return []; } + private getLayersByKey(key: number): Array { + return this.flatLayers.filter(flatLayer => flatLayer.type === 'tilelayer' && flatLayer.data[key] !== 0); + } + /** * Sets the position of the current player (in pixels) * This will trigger events if properties are changing. */ public setPosition(x: number, y: number) { + this.oldKey = this.key; + const xMap = Math.floor(x / this.map.tilewidth); const yMap = Math.floor(y / this.map.tileheight); const key = xMap + yMap * this.map.width; + if (key === this.key) { return; } + this.key = key; - this.triggerAll(); + + this.triggerAllProperties(); + this.triggerLayersChange(); } - private triggerAll(): void { + private triggerAllProperties(): void { const newProps = this.getProperties(this.key ?? 0); const oldProps = this.lastProperties; this.lastProperties = newProps; @@ -105,6 +127,36 @@ export class GameMap { } } + private triggerLayersChange() { + const layersByOldKey = this.oldKey ? this.getLayersByKey(this.oldKey) : []; + const layersByNewKey = this.key ? this.getLayersByKey(this.key) : []; + + const enterLayers = new Set(layersByNewKey); + const leaveLayers = new Set(layersByOldKey); + + enterLayers.forEach(layer => { + if (leaveLayers.has(layer)) { + leaveLayers.delete(layer); + enterLayers.delete(layer); + } + }); + + + if (enterLayers.size > 0) { + const layerArray = Array.from(enterLayers); + for (const callback of this.enterLayerCallbacks) { + callback(layerArray, layersByNewKey); + } + } + + if (leaveLayers.size > 0) { + const layerArray = Array.from(leaveLayers); + for (const callback of this.leaveLayerCallbacks) { + callback(layerArray, layersByNewKey); + } + } + } + public getCurrentProperties(): Map { return this.lastProperties; } @@ -167,7 +219,7 @@ export class GameMap { newValue: string | number | boolean | undefined, allProps: Map ) { - const callbacksArray = this.callbacks.get(propName); + const callbacksArray = this.propertiesChangeCallbacks.get(propName); if (callbacksArray !== undefined) { for (const callback of callbacksArray) { callback(newValue, oldValue, allProps); @@ -179,14 +231,28 @@ export class GameMap { * Registers a callback called when the user moves to a tile where the property propName is different from the last tile the user was on. */ public onPropertyChange(propName: string, callback: PropertyChangeCallback) { - let callbacksArray = this.callbacks.get(propName); + let callbacksArray = this.propertiesChangeCallbacks.get(propName); if (callbacksArray === undefined) { callbacksArray = new Array(); - this.callbacks.set(propName, callbacksArray); + this.propertiesChangeCallbacks.set(propName, callbacksArray); } callbacksArray.push(callback); } + /** + * Registers a callback called when the user moves inside another layer. + */ + public onEnterLayer(callback: layerChangeCallback) { + this.enterLayerCallbacks.push(callback); + } + + /** + * Registers a callback called when the user moves outside another layer. + */ + public onLeaveLayer(callback: layerChangeCallback) { + this.leaveLayerCallbacks.push(callback); + } + public findLayer(layerName: string): ITiledMapLayer | undefined { return this.flatLayers.find((layer) => layer.name === layerName); } @@ -284,7 +350,8 @@ export class GameMap { } property.value = propertyValue; - this.triggerAll(); + this.triggerAllProperties(); + this.triggerLayersChange(); } /** diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index d65c4d54..c8858ad3 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -186,6 +186,8 @@ export class GameScene extends DirtyScene { moving: false, x: -1000, y: -1000, + oldX: -1000, + oldY: -1000, }; private gameMap!: GameMap; @@ -764,6 +766,19 @@ export class GameScene extends DirtyScene { //init user position and play trigger to check layers properties this.gameMap.setPosition(this.CurrentPlayer.x, this.CurrentPlayer.y); + + // Init layer change listener + this.gameMap.onEnterLayer(layers => { + layers.forEach(layer => { + iframeListener.sendEnterLayerEvent(layer.name); + }); + }); + + this.gameMap.onLeaveLayer(layers => { + layers.forEach(layer => { + iframeListener.sendLeaveLayerEvent(layer.name); + }); + }); }); } @@ -895,6 +910,7 @@ export class GameScene extends DirtyScene { audioManagerVisibilityStore.set(!(newValue === undefined)); }); + // TODO: Legacy functionnality replace by layer change this.gameMap.onPropertyChange("zone", (newValue, oldValue) => { if (oldValue) { iframeListener.sendLeaveEvent(oldValue as string); @@ -1749,7 +1765,11 @@ ${escapedMessage} const playerMovement = new PlayerMovement( { x: player.x, y: player.y }, this.currentTick, - message.position, + { + ...message.position, + oldX: undefined, + oldY: undefined, + }, this.currentTick + POSITION_DELAY ); this.playersPositionInterpolator.updatePlayerPosition(player.userId, playerMovement); diff --git a/front/src/Phaser/Game/PlayerMovement.ts b/front/src/Phaser/Game/PlayerMovement.ts index c3daedad..7758f010 100644 --- a/front/src/Phaser/Game/PlayerMovement.ts +++ b/front/src/Phaser/Game/PlayerMovement.ts @@ -38,6 +38,8 @@ export class PlayerMovement { return { x, y, + oldX: this.startPosition.x, + oldY: this.startPosition.y, direction: this.endPosition.direction, moving: true, }; diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts index 3edcdcde..28a1d3bd 100644 --- a/front/src/Phaser/Player/Player.ts +++ b/front/src/Phaser/Player/Player.ts @@ -64,14 +64,14 @@ export class Player extends Character { if (x !== 0 || y !== 0) { this.move(x, y); - this.emit(hasMovedEventName, { moving, direction, x: this.x, y: this.y }); + this.emit(hasMovedEventName, { moving, direction, x: this.x, y: this.y, oldX: x, oldY: y }); } else if (this.wasMoving && moving) { // slow joystick movement this.move(0, 0); - this.emit(hasMovedEventName, { moving, direction: this.previousDirection, x: this.x, y: this.y }); + this.emit(hasMovedEventName, { moving, direction: this.previousDirection, x: this.x, y: this.y, oldX: x, oldY: y }); } else if (this.wasMoving && !moving) { this.stop(); - this.emit(hasMovedEventName, { moving, direction: this.previousDirection, x: this.x, y: this.y }); + this.emit(hasMovedEventName, { moving, direction: this.previousDirection, x: this.x, y: this.y, oldX: x, oldY: y }); } if (direction !== null) { diff --git a/front/src/Stores/LayoutManagerStore.ts b/front/src/Stores/LayoutManagerStore.ts index e92cd3c4..063d45a7 100644 --- a/front/src/Stores/LayoutManagerStore.ts +++ b/front/src/Stores/LayoutManagerStore.ts @@ -9,7 +9,9 @@ export interface LayoutManagerAction { userInputManager: UserInputManager | undefined; } + function createLayoutManagerAction() { + const { subscribe, set, update } = writable([]); return { diff --git a/front/tests/Phaser/Game/PlayerMovementTest.ts b/front/tests/Phaser/Game/PlayerMovementTest.ts index ce2e2767..4b9e8e99 100644 --- a/front/tests/Phaser/Game/PlayerMovementTest.ts +++ b/front/tests/Phaser/Game/PlayerMovementTest.ts @@ -7,7 +7,12 @@ describe("Interpolation / Extrapolation", () => { x: 100, y: 200 }, 42000, { - x: 200, y: 100, moving: true, direction: "up" + x: 200, + y: 100, + oldX: undefined, + oldY: undefined, + moving: true, + direction: "up" }, 42200 ); @@ -19,6 +24,8 @@ describe("Interpolation / Extrapolation", () => { expect(playerMovement.getPosition(42100)).toEqual({ x: 150, y: 150, + oldX: undefined, + oldY: undefined, direction: 'up', moving: true }); @@ -26,6 +33,8 @@ describe("Interpolation / Extrapolation", () => { expect(playerMovement.getPosition(42200)).toEqual({ x: 200, y: 100, + oldX: undefined, + oldY: undefined, direction: 'up', moving: true }); @@ -33,6 +42,8 @@ describe("Interpolation / Extrapolation", () => { expect(playerMovement.getPosition(42300)).toEqual({ x: 250, y: 50, + oldX: undefined, + oldY: undefined, direction: 'up', moving: true }); @@ -43,7 +54,12 @@ describe("Interpolation / Extrapolation", () => { x: 100, y: 200 }, 42000, { - x: 200, y: 100, moving: false, direction: "up" + x: 200, + y: 100, + oldX: undefined, + oldY: undefined, + moving: false, + direction: "up" }, 42200 ); @@ -51,6 +67,8 @@ describe("Interpolation / Extrapolation", () => { expect(playerMovement.getPosition(42300)).toEqual({ x: 200, y: 100, + oldX: undefined, + oldY: undefined, direction: 'up', moving: false }); @@ -61,7 +79,12 @@ describe("Interpolation / Extrapolation", () => { x: 100, y: 200 }, 42000, { - x: 200, y: 100, moving: false, direction: "up" + x: 200, + y: 100, + oldX: undefined, + oldY: undefined, + moving: false, + direction: "up" }, 42200 ); @@ -69,6 +92,8 @@ describe("Interpolation / Extrapolation", () => { expect(playerMovement.getPosition(42100)).toEqual({ x: 150, y: 150, + oldX: undefined, + oldY: undefined, direction: 'up', moving: true }); diff --git a/pusher/src/Services/SocketManager.ts b/pusher/src/Services/SocketManager.ts index 119596fd..1761f1bd 100644 --- a/pusher/src/Services/SocketManager.ts +++ b/pusher/src/Services/SocketManager.ts @@ -231,12 +231,12 @@ export class SocketManager implements ZoneEventListener { try { client.viewport = viewport; - const world = this.rooms.get(client.roomId); - if (!world) { + const room = this.rooms.get(client.roomId); + if (!room) { console.error("In SET_VIEWPORT, could not find world with id '", client.roomId, "'"); return; } - world.setViewport(client, client.viewport); + room.setViewport(client, client.viewport); } catch (e) { console.error('An error occurred on "SET_VIEWPORT" event'); console.error(e); From f4df12e5ff898b3ba51b9568c09a0253de023ca9 Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Fri, 29 Oct 2021 16:47:19 +0200 Subject: [PATCH 3/5] Cowebsite properties manage by layer and not by property index --- .../Phaser/Game/GameMapPropertiesListener.ts | 195 ++++++++++++++++-- 1 file changed, 174 insertions(+), 21 deletions(-) diff --git a/front/src/Phaser/Game/GameMapPropertiesListener.ts b/front/src/Phaser/Game/GameMapPropertiesListener.ts index 29084d2a..3ae6d714 100644 --- a/front/src/Phaser/Game/GameMapPropertiesListener.ts +++ b/front/src/Phaser/Game/GameMapPropertiesListener.ts @@ -1,15 +1,32 @@ 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 { get } from 'svelte/store'; import { ON_ACTION_TRIGGER_BUTTON, TRIGGER_WEBSITE_PROPERTIES, WEBSITE_MESSAGE_PROPERTIES, } from "../../WebRtc/LayoutManager"; +import type { ITiledMapLayer } from "../Map/ITiledMap"; + +enum OpenCoWebsiteState { + LOADING, + OPENED, + MUST_BE_CLOSE, +} + +interface OpenCoWebsite { + coWebsite: CoWebsite | undefined, + state: OpenCoWebsiteState +} export class GameMapPropertiesListener { + private coWebsitesOpenByLayer = new Map(); + private coWebsitesActionTriggerByLayer = new Map(); + constructor(private scene: GameScene, private gameMap: GameMap) {} register() { @@ -36,42 +53,178 @@ export class GameMapPropertiesListener { } } }); - this.gameMap.onPropertyChange("openWebsite", (newValue, oldValue, allProps) => { - const handler = async () => { - if (newValue === undefined || newValue !== oldValue) { - layoutManagerActionStore.removeAction("openWebsite"); - await coWebsiteManager.closeCoWebsites(); - } - if (newValue !== undefined) { + // Open a new co-website by the property. + this.gameMap.onEnterLayer((newLayers) => { + const handler = () => { + newLayers.forEach(layer => { + if (!layer.properties) { + return; + } + + let openWebsiteProperty: string | undefined; + let allowApiProperty: boolean | undefined; + let websitePolicyProperty: string | undefined; + let websiteWidthProperty: number | undefined; + let websitePositionProperty: number | undefined; + let websiteTriggerProperty: string | undefined; + let websiteTriggerMessageProperty: string | undefined; + + layer.properties.forEach(property => { + switch(property.name) { + case 'openWebsite': + openWebsiteProperty = property.value as string | undefined; + break; + case 'openWebsiteAllowApi': + allowApiProperty = property.value as boolean | undefined; + break; + case 'openWebsitePolicy': + websitePolicyProperty = property.value as string | undefined; + break; + case 'openWebsiteWidth': + websiteWidthProperty = property.value as number | undefined; + break; + case 'openWebsitePosition': + websitePositionProperty = property.value as number | undefined; + break; + case TRIGGER_WEBSITE_PROPERTIES: + websiteTriggerProperty = property.value as string | undefined; + break; + case WEBSITE_MESSAGE_PROPERTIES: + websiteTriggerMessageProperty = property.value as string | undefined; + break; + } + }); + + if (!openWebsiteProperty) { + return; + } + + const actionUuid = "openWebsite-" + (Math.random() + 1).toString(36).substring(7); + + if (this.coWebsitesOpenByLayer.has(layer)) { + return; + } + + this.coWebsitesOpenByLayer.set(layer, { + coWebsite: undefined, + state: OpenCoWebsiteState.LOADING, + }); + const openWebsiteFunction = () => { coWebsiteManager.loadCoWebsite( - newValue as string, + openWebsiteProperty as string, this.scene.MapUrlFile, - allProps.get("openWebsiteAllowApi") as boolean | undefined, - allProps.get("openWebsitePolicy") as string | undefined, - allProps.get("openWebsiteWidth") as number | undefined - ); + allowApiProperty, + websitePolicyProperty, + websiteWidthProperty, + websitePositionProperty, + ).then(coWebsite => { + const coWebsiteOpen = this.coWebsitesOpenByLayer.get(layer); + if (coWebsiteOpen && coWebsiteOpen.state === OpenCoWebsiteState.MUST_BE_CLOSE) { + coWebsiteManager.closeCoWebsite(coWebsite); + this.coWebsitesOpenByLayer.delete(layer); + this.coWebsitesActionTriggerByLayer.delete(layer); + } else { + this.coWebsitesOpenByLayer.set(layer, { + coWebsite, + state: OpenCoWebsiteState.OPENED + }); + } + }); - layoutManagerActionStore.removeAction("openWebsite"); + layoutManagerActionStore.removeAction(actionUuid); }; - const openWebsiteTriggerValue = allProps.get(TRIGGER_WEBSITE_PROPERTIES); - if (openWebsiteTriggerValue && openWebsiteTriggerValue === ON_ACTION_TRIGGER_BUTTON) { - let message = allProps.get(WEBSITE_MESSAGE_PROPERTIES); - if (message === undefined) { - message = "Press SPACE or touch here to open web site"; + + if (websiteTriggerProperty && websiteTriggerProperty === ON_ACTION_TRIGGER_BUTTON) { + if (!websiteTriggerMessageProperty) { + websiteTriggerMessageProperty = "Press SPACE or touch here to open web site"; } + + this.coWebsitesActionTriggerByLayer.set(layer, actionUuid); + layoutManagerActionStore.addAction({ - uuid: "openWebsite", + uuid: actionUuid, type: "message", - message: message, + message: websiteTriggerMessageProperty, callback: () => openWebsiteFunction(), userInputManager: this.scene.userInputManager, }); } else { openWebsiteFunction(); } - } + }); + }; + + handler(); + }); + + // Close opened co-websites on leave the layer who contain the property. + this.gameMap.onLeaveLayer((oldLayers) => { + const handler = () => { + oldLayers.forEach(layer => { + if (!layer.properties) { + return; + } + + let openWebsiteProperty: string | undefined; + let websiteTriggerProperty: string | undefined; + + layer.properties.forEach(property => { + switch(property.name) { + case 'openWebsite': + openWebsiteProperty = property.value as string | undefined; + break; + case TRIGGER_WEBSITE_PROPERTIES: + websiteTriggerProperty = property.value as string | undefined; + break; + } + }); + + if (!openWebsiteProperty) { + return; + } + + const coWebsiteOpen = this.coWebsitesOpenByLayer.get(layer); + + if (!coWebsiteOpen) { + return; + } + + if (coWebsiteOpen.state === OpenCoWebsiteState.LOADING) { + coWebsiteOpen.state = OpenCoWebsiteState.MUST_BE_CLOSE; + return; + } + + if (coWebsiteOpen.state !== OpenCoWebsiteState.OPENED) { + return; + } + + if (coWebsiteOpen.coWebsite !== undefined) { + coWebsiteManager.closeCoWebsite(coWebsiteOpen.coWebsite); + } + + this.coWebsitesOpenByLayer.delete(layer); + + if (!websiteTriggerProperty) { + return; + } + + const actionStore = get(layoutManagerActionStore); + const actionTriggerUuid = this.coWebsitesActionTriggerByLayer.get(layer); + + if (!actionTriggerUuid) { + return; + } + + const action = actionStore && actionStore.length > 0 ? + actionStore.find(action => action.uuid === actionTriggerUuid) : undefined; + + + if (action) { + layoutManagerActionStore.removeAction(actionTriggerUuid); + } + }); }; handler(); From db82ae4b88322168cf984fcb783818b706fa5327 Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Fri, 29 Oct 2021 17:15:20 +0200 Subject: [PATCH 4/5] Replace the enter/leave zone by enter/leave layer on doc --- docs/maps/api-deprecated.md | 31 +++++++++++++++--------------- docs/maps/api-room.md | 38 +++++++++++++++---------------------- 2 files changed, 31 insertions(+), 38 deletions(-) diff --git a/docs/maps/api-deprecated.md b/docs/maps/api-deprecated.md index cc96e12a..ffa8af9e 100644 --- a/docs/maps/api-deprecated.md +++ b/docs/maps/api-deprecated.md @@ -3,20 +3,21 @@ The list of functions below is **deprecated**. You should not use those but. use the replacement functions. -- Method `WA.sendChatMessage` is deprecated. It has been renamed to `WA.chat.sendChatMessage`. -- Method `WA.disablePlayerControls` is deprecated. It has been renamed to `WA.controls.disablePlayerControls`. -- Method `WA.restorePlayerControls` is deprecated. It has been renamed to `WA.controls.restorePlayerControls`. +- Method `WA.sendChatMessage` is deprecated. It has been renamed to [`WA.chat.sendChatMessage`](api-chat.md#sending-a-message-in-the-chat). +- Method `WA.disablePlayerControls` is deprecated. It has been renamed to [`WA.controls.disablePlayerControls`](api-controls.md#disabling--restoring-controls). +- Method `WA.restorePlayerControls` is deprecated. It has been renamed to [`WA.controls.restorePlayerControls`](api-controls.md#disabling--restoring-controls). - Method `WA.displayBubble` is deprecated. It has been renamed to `WA.ui.displayBubble`. - Method `WA.removeBubble` is deprecated. It has been renamed to `WA.ui.removeBubble`. -- Method `WA.openTab` is deprecated. It has been renamed to `WA.nav.openTab`. -- Method `WA.loadSound` is deprecated. It has been renamed to `WA.sound.loadSound`. -- Method `WA.goToPage` is deprecated. It has been renamed to `WA.nav.goToPage`. -- Method `WA.goToRoom` is deprecated. It has been renamed to `WA.nav.goToRoom`. -- Method `WA.openCoWebSite` is deprecated. It has been renamed to `WA.nav.openCoWebSite`. -- Method `WA.closeCoWebSite` is deprecated. It has been renamed to `WA.nav.closeCoWebSite`. -- Method `WA.closeCoWebsite` is deprecated. It has been renamed to `WA.nav.closeCoWebsite`. -- Method `WA.openPopup` is deprecated. It has been renamed to `WA.ui.openPopup`. -- Method `WA.onChatMessage` is deprecated. It has been renamed to `WA.chat.onChatMessage`. -- Method `WA.onEnterZone` is deprecated. It has been renamed to `WA.room.onEnterZone`. -- Method `WA.onLeaveZone` is deprecated. It has been renamed to `WA.room.onLeaveZone`. -- Method `WA.ui.registerMenuCommand` parameter `callback` is deprecated. Use `WA.ui.registerMenuCommand(commandDescriptor: string, options: MenuOptions)`. \ No newline at end of file +- Method `WA.openTab` is deprecated. It has been renamed to [`WA.nav.openTab`](api-nav.md#opening-a-web-page-in-a-new-tab). +- Method `WA.loadSound` is deprecated. It has been renamed to [`WA.sound.loadSound`](api-sound.md#load-a-sound-from-an-url). +- Method `WA.goToPage` is deprecated. It has been renamed to [`WA.nav.goToPage`](api-nav.md#opening-a-web-page-in-the-current-tab). +- Method `WA.goToRoom` is deprecated. It has been renamed to [`WA.nav.goToRoom`](api-nav.md#going-to-a-different-map-from-the-script). +- Method `WA.openCoWebSite` is deprecated. It has been renamed to [`WA.nav.openCoWebSite`](api-nav.md#openingclosing-web-page-in-co-websites). +- Method `WA.closeCoWebSite` is deprecated. It has been remove and [replace by a function close](api-nav.md#openingclosing-web-page-in-co-websites). +- Method `WA.openPopup` is deprecated. It has been renamed to [`WA.ui.openPopup`](api-ui.md#opening-a-popup). +- Method `WA.onChatMessage` is deprecated. It has been renamed to [`WA.chat.onChatMessage`](api-chat.md#listening-to-messages-from-the-chat). +- Method `WA.onEnterZone` is deprecated. It has been renamed to [`WA.room.onEnterZone`](api-room.md#detecting-when-the-user-entersleaves-a-layer). +- Method `WA.onLeaveZone` is deprecated. It has been renamed to [`WA.room.onLeaveZone`](api-room.md#detecting-when-the-user-entersleaves-a-layer). +- Method `WA.ui.registerMenuCommand` parameter `callback` is deprecated. Use [`WA.ui.registerMenuCommand(commandDescriptor: string, options: MenuOptions)`](api-ui.md#add-custom-menu). +- Method `WA.room.onEnterZone` is deprecated. Use instead [`WA.room.onEnterLayer`](api-room.md#detecting-when-the-user-entersleaves-a-layer). +- Method `WA.room.onLeaveZone` is deprecated. Use instead [`WA.room.onLeaveLayer`](api-room.md#detecting-when-the-user-entersleaves-a-layer). \ No newline at end of file diff --git a/docs/maps/api-room.md b/docs/maps/api-room.md index d1a26d2f..72947df8 100644 --- a/docs/maps/api-room.md +++ b/docs/maps/api-room.md @@ -17,35 +17,27 @@ The name of the layers of this map are : * `bottom/build/carpet` * `wall` -### Detecting when the user enters/leaves a zone +### Detecting when the user enters/leaves a layer ``` -WA.room.onEnterZone(name: string, callback: () => void): void -WA.room.onLeaveZone(name: string, callback: () => void): void +WA.room.onEnterLayer(name: string): Subscription +WA.room.onLeaveLayer(name: string): Subscription ``` -Listens to the position of the current user. The event is triggered when the user enters or leaves a given zone. The name of the zone is stored in the map, on a dedicated layer with the `zone` property. +Listens to the position of the current user. The event is triggered when the user enters or leaves a given layer. -
-
- -
The `zone` property, applied on a layer
-
-
- -* **name**: the name of the zone, as defined in the `zone` property. -* **callback**: the function that will be called when a user enters or leaves the zone. +* **name**: the name of the layer who as defined in Tiled. Example: ```javascript -WA.room.onEnterZone('myZone', () => { +WA.room.onEnterLayer('myLayer').subscribe(() => { WA.chat.sendChatMessage("Hello!", 'Mr Robot'); -}) +}); -WA.room.onLeaveZone('myZone', () => { +WA.room.onLeaveLayer('myLayer').subscribe(() => { WA.chat.sendChatMessage("Goodbye!", 'Mr Robot'); -}) +}); ``` ### Show / Hide a layer @@ -71,7 +63,7 @@ WA.room.setProperty(layerName : string, propertyName : string, propertyValue : s Set the value of the `propertyName` property of the layer `layerName` at `propertyValue`. If the property doesn't exist, create the property `propertyName` and set the value of the property at `propertyValue`. -Note : +Note : To unset a property from a layer, use `setProperty` with `propertyValue` set to `undefined`. Example : @@ -131,7 +123,7 @@ console.log("Map generated with Tiled version ", map.tiledversion); Check the [Tiled documentation to learn more about the format of the JSON map](https://doc.mapeditor.org/en/stable/reference/json-map-format/). -### Changing tiles +### Changing tiles ``` WA.room.setTiles(tiles: TileDescriptor[]): void ``` @@ -144,7 +136,7 @@ If `tile` is a string, it's not the id of the tile but the value of the property -`TileDescriptor` has the following attributes : +`TileDescriptor` has the following attributes : * **x (number) :** The coordinate x of the tile that you want to replace. * **y (number) :** The coordinate y of the tile that you want to replace. * **tile (number | string) :** The id of the tile that will be placed in the map. @@ -154,7 +146,7 @@ If `tile` is a string, it's not the id of the tile but the value of the property Note: If you want to unset a tile, use `setTiles` with `tile` set to `null`. -Example : +Example : ```javascript WA.room.setTiles([ {x: 6, y: 4, tile: 'blue', layer: 'setTiles'}, @@ -246,7 +238,7 @@ const website = WA.room.website.create({ WA.room.website.delete(name: string): Promise ``` -Use `WA.room.website.delete` to completely remove an embedded website from your map. +Use `WA.room.website.delete` to completely remove an embedded website from your map. ### The EmbeddedWebsite class @@ -271,7 +263,7 @@ When you modify a property of an `EmbeddedWebsite` instance, the iframe is autom {.alert.alert-warning} -The websites you add/edit/delete via the scripting API are only shown locally. If you want them +The websites you add/edit/delete via the scripting API are only shown locally. If you want them to be displayed for every player, you can use [variables](api-start.md) to share a common state between all users. From e1d454d834df0a0a51a198debea08f6cfdc6fd28 Mon Sep 17 00:00:00 2001 From: Alexis Faizeau Date: Fri, 29 Oct 2021 17:37:29 +0200 Subject: [PATCH 5/5] Add enters/leaves layer with api test map --- .../ChangeLayerApi/change_layer_api.json | 617 ++++++++++++++++++ maps/tests/ChangeLayerApi/script.js | 7 + maps/tests/index.html | 8 + 3 files changed, 632 insertions(+) create mode 100644 maps/tests/ChangeLayerApi/change_layer_api.json create mode 100644 maps/tests/ChangeLayerApi/script.js diff --git a/maps/tests/ChangeLayerApi/change_layer_api.json b/maps/tests/ChangeLayerApi/change_layer_api.json new file mode 100644 index 00000000..35c25683 --- /dev/null +++ b/maps/tests/ChangeLayerApi/change_layer_api.json @@ -0,0 +1,617 @@ +{ "compressionlevel":-1, + "height":10, + "infinite":false, + "layers":[ + { + "data":[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], + "height":10, + "id":1, + "name":"floor", + "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, 12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + "height":10, + "id":2, + "name":"start", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23, 0, 0, 0, 0, 0, 23, 23, 23, 23, 23], + "height":10, + "id":5, + "name":"myLayer", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }, + { + "draworder":"topdown", + "id":3, + "name":"floorLayer", + "objects":[ + { + "height":111.874771331266, + "id":1, + "name":"Tests", + "rotation":0, + "text": + { + "fontfamily":"Sans Serif", + "pixelsize":8, + "text":"Test 1:\nGo on the blue carpet.\nResult:\nA message has been sent to the chat.\n\nTest 2:\nGo outside the blue carpet.\nResult:\nAnother message has been sent to the chat.\n", + "wrap":true + }, + "type":"", + "visible":true, + "width":316.770833333333, + "x":1.64026713939023, + "y":206.086424886945 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }, + { + "data":[0, 0, 0, 0, 0, 0, 0, 0, 82, 0, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 27, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":8, + "name":"objects", + "opacity":1, + "type":"tilelayer", + "visible":true, + "width":10, + "x":0, + "y":0 + }], + "nextlayerid":9, + "nextobjectid":3, + "orientation":"orthogonal", + "properties":[ + { + "name":"script", + "type":"string", + "value":"script.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, + "tiles":[ + { + "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":5, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":6, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":7, + "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":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 + }] + }, + { + "id":21, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":23, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":24, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":25, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":26, + "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":29, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":30, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":31, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":32, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":34, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":35, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":42, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":43, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":45, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":46, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":59, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":60, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":70, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":71, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":80, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":81, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":89, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":91, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":93, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":94, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":95, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":96, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":97, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":100, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":102, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":103, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":104, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":105, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":106, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":107, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":108, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":114, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }, + { + "id":115, + "properties":[ + { + "name":"collides", + "type":"bool", + "value":true + }] + }], + "tilewidth":32 + }], + "tilewidth":32, + "type":"map", + "version":"1.6", + "width":10 +} \ No newline at end of file diff --git a/maps/tests/ChangeLayerApi/script.js b/maps/tests/ChangeLayerApi/script.js new file mode 100644 index 00000000..32f038ba --- /dev/null +++ b/maps/tests/ChangeLayerApi/script.js @@ -0,0 +1,7 @@ +WA.room.onEnterLayer('myLayer').subscribe(() => { + WA.chat.sendChatMessage("Hello!", 'Wooka'); +}); + +WA.room.onLeaveLayer('myLayer').subscribe(() => { + WA.chat.sendChatMessage("Goodbye!", 'Wooka'); +}); \ No newline at end of file diff --git a/maps/tests/index.html b/maps/tests/index.html index 578e8ea6..068136ed 100644 --- a/maps/tests/index.html +++ b/maps/tests/index.html @@ -235,6 +235,14 @@ Testing scripting API for websites inside a map + + + Success Failure Pending + + + Testing scripting API for enters/leaves layer + +

CoWebsite