diff --git a/docs/maps/api-chat.md b/docs/maps/api-chat.md new file mode 100644 index 00000000..49a40f97 --- /dev/null +++ b/docs/maps/api-chat.md @@ -0,0 +1,37 @@ +{.section-title.accent.text-primary} +# API Chat functions reference + +### Sending a message in the chat + +``` +WA.chat.sendChatMessage(message: string, author: string): void +``` + +Sends a message in the chat. The message is only visible in the browser of the current user. + +* **message**: the message to be displayed in the chat +* **author**: the name displayed for the author of the message. It does not have to be a real user. + +Example: + +```javascript +WA.chat.sendChatMessage('Hello world', 'Mr Robot'); +``` + +### Listening to messages from the chat + +```javascript +WA.chat.onChatMessage(callback: (message: string) => void): void +``` + +Listens to messages typed by the current user and calls the callback. Messages from other users in the chat cannot be listened to. + +* **callback**: the function that will be called when a message is received. It contains the message typed by the user. + +Example: + +```javascript +WA.chat.onChatMessage((message => { + console.log('The user typed a message', message); +})); +``` diff --git a/docs/maps/api-controls.md b/docs/maps/api-controls.md new file mode 100644 index 00000000..dcb0f17b --- /dev/null +++ b/docs/maps/api-controls.md @@ -0,0 +1,29 @@ +{.section-title.accent.text-primary} +# API Controls functions Reference + +### Disabling / restoring controls + +``` +WA.controls.disablePlayerControls(): void +WA.controls.restorePlayerControls(): void +``` + +These 2 methods can be used to completely disable player controls and to enable them again. + +When controls are disabled, the user cannot move anymore using keyboard input. This can be useful in a "First Time User Experience" part, to display an important message to a user before letting him/her move again. + +Example: + +```javascript +WA.room.onEnterZone('myZone', () => { + WA.controls.disablePlayerControls(); + WA.ui.openPopup("popupRectangle", 'This is an imporant message!', [{ + label: "Got it!", + className: "primary", + callback: (popup) => { + WA.controls.restorePlayerControls(); + popup.close(); + } + }]); +}); +``` diff --git a/docs/maps/api-deprecated.md b/docs/maps/api-deprecated.md new file mode 100644 index 00000000..930caebe --- /dev/null +++ b/docs/maps/api-deprecated.md @@ -0,0 +1,20 @@ +{.section-title.accent.text-primary} +# API Reference - Deprecated functions + +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.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.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`. diff --git a/docs/maps/api-nav.md b/docs/maps/api-nav.md new file mode 100644 index 00000000..29323632 --- /dev/null +++ b/docs/maps/api-nav.md @@ -0,0 +1,68 @@ +{.section-title.accent.text-primary} +# API Navigation functions reference + +### Opening a web page in a new tab + +``` +WA.nav.openTab(url: string): void +``` + +Opens the webpage at "url" in your browser, in a new tab. + +Example: + +```javascript +WA.nav.openTab('https://www.wikipedia.org/'); +``` + +### Opening a web page in the current tab + +``` +WA.nav.goToPage(url: string): void +``` + +Opens the webpage at "url" in your browser in place of WorkAdventure. WorkAdventure will be completely unloaded. + +Example: + +```javascript +WA.nav.goToPage('https://www.wikipedia.org/'); +``` + +### Going to a different map from the script + +``` + +WA.nav.goToRoom(url: string): void +``` + +Load the map at url without unloading workadventure + +relative urls: "../subFolder/map.json[#start-layer-name]" +global urls: "/_/global/domain/path/map.json[#start-layer-name]" + +Example: + +```javascript +WA.nav.goToRoom("/@/tcm/workadventure/floor0") // workadventure urls +WA.nav.goToRoom('../otherMap/map.json'); +WA.nav.goToRoom("/_/global/.json#start-layer-2") +``` + +### Opening/closing a web page in an iFrame + +``` +WA.nav.openCoWebSite(url: string): void +WA.nav.closeCoWebSite(): void +``` + +Opens the webpage at "url" in an iFrame (on the right side of the screen) or close that iFrame. + +Example: + +```javascript +WA.nav.openCoWebSite('https://www.wikipedia.org/'); +// ... +WA.nav.closeCoWebSite(); +``` + diff --git a/docs/maps/api-reference.md b/docs/maps/api-reference.md index b9525d3c..251edfa2 100644 --- a/docs/maps/api-reference.md +++ b/docs/maps/api-reference.md @@ -1,257 +1,11 @@ {.section-title.accent.text-primary} # API Reference -### Sending a message in the chat - -``` -sendChatMessage(message: string, author: string): void -``` - -Sends a message in the chat. The message is only visible in the browser of the current user. - -* **message**: the message to be displayed in the chat -* **author**: the name displayed for the author of the message. It does not have to be a real user. - -Example: - -```javascript -WA.sendChatMessage('Hello world', 'Mr Robot'); -``` - -### Listening to messages from the chat - -```javascript -onChatMessage(callback: (message: string) => void): void -``` - -Listens to messages typed by the current user and calls the callback. Messages from other users in the chat cannot be listened to. - -* **callback**: the function that will be called when a message is received. It contains the message typed by the user. - -Example: - -```javascript -WA.onChatMessage((message => { - console.log('The user typed a message', message); -})); -``` - -### Detecting when the user enters/leaves a zone - -``` -onEnterZone(name: string, callback: () => void): void -onLeaveZone(name: string, callback: () => void): void -``` - -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. - -
-
- -
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. - -Example: - -```javascript -WA.onEnterZone('myZone', () => { - WA.sendChatMessage("Hello!", 'Mr Robot'); -}) - -WA.onLeaveZone('myZone', () => { - WA.sendChatMessage("Goodbye!", 'Mr Robot'); -}) -``` - -### Opening a popup - -In order to open a popup window, you must first define the position of the popup on your map. - -You can position this popup by using a "rectangle" object in Tiled that you will place on an "object" layer. - -
-
- -
-
- -
-
- -``` -openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup -``` - -* **targetObject**: the name of the rectangle object defined in Tiled. -* **message**: the message to display in the popup. -* **buttons**: an array of action buttons defined underneath the popup. - -Action buttons are `ButtonDescriptor` objects containing these properties. - -* **label (_string_)**: The label of the button. -* **className (_string_)**: The visual type of the button. Can be one of "normal", "primary", "success", "warning", "error", "disabled". -* **callback (_(popup: Popup)=>void_)**: Callback called when the button is pressed. - -Please note that `openPopup` returns an object of the `Popup` class. Also, the callback called when a button is clicked is passed a `Popup` object. - -The `Popup` class that represents an open popup contains a single method: `close()`. This will obviously close the popup when called. - -```javascript -class Popup { - /** - * Closes the popup - */ - close() {}; -} -``` - -Example: - -```javascript -let helloWorldPopup; - -// Open the popup when we enter a given zone -helloWorldPopup = WA.onEnterZone('myZone', () => { - WA.openPopup("popupRectangle", 'Hello world!', [{ - label: "Close", - className: "primary", - callback: (popup) => { - // Close the popup when the "Close" button is pressed. - popup.close(); - } - }); -}]); - -// Close the popup when we leave the zone. -WA.onLeaveZone('myZone', () => { - helloWorldPopup.close(); -}); -``` - -### Disabling / restoring controls - -``` -disablePlayerControls(): void -restorePlayerControls(): void -``` - -These 2 methods can be used to completely disable player controls and to enable them again. - -When controls are disabled, the user cannot move anymore using keyboard input. This can be useful in a "First Time User Experience" part, to display an important message to a user before letting him/her move again. - -Example: - -```javascript -WA.onEnterZone('myZone', () => { - WA.disablePlayerControls(); - WA.openPopup("popupRectangle", 'This is an imporant message!', [{ - label: "Got it!", - className: "primary", - callback: (popup) => { - WA.restorePlayerControls(); - popup.close(); - } - }]); -}); -``` - -### Opening a web page in a new tab - -``` -openTab(url: string): void -``` - -Opens the webpage at "url" in your browser, in a new tab. - -Example: - -```javascript -WA.openTab('https://www.wikipedia.org/'); -``` - -### Opening a web page in the current tab - -``` -goToPage(url: string): void -``` - -Opens the webpage at "url" in your browser in place of WorkAdventure. WorkAdventure will be completely unloaded. - -Example: - -```javascript -WA.goToPage('https://www.wikipedia.org/'); -``` - -### Going to a different map from the script - -``` - -goToRoom(url: string): void -``` - -Load the map at url without unloading workadventure - -relative urls: "../subFolder/map.json[#start-layer-name]" -global urls: "/_/global/domain/path/map.json[#start-layer-name]" - -Example: - -```javascript -WA.goToRoom("/@/tcm/workadventure/floor0") // workadventure urls -WA.goToRoom('../otherMap/map.json'); -WA.goToRoom("/_/global/.json#start-layer-2") -``` - -### Opening/closing a web page in an iFrame - -``` -openCoWebSite(url: string): void -closeCoWebSite(): void -``` - -Opens the webpage at "url" in an iFrame (on the right side of the screen) or close that iFrame. - -Example: - -```javascript -WA.openCoWebSite('https://www.wikipedia.org/'); -// ... -WA.closeCoWebSite(); -``` - -### Load a sound from an url - -``` -loadSound(url: string): Sound -``` - -Load a sound from an url - -Please note that `loadSound` returns an object of the `Sound` class - -The `Sound` class that represents a loaded sound contains two methods: `play(soundConfig : SoundConfig|undefined)` and `stop()` - -The parameter soundConfig is optional, if you call play without a Sound config the sound will be played with the basic configuration. - -Example: - -```javascript -var mySound = WA.loadSound("Sound.ogg"); -var config = { - volume : 0.5, - loop : false, - rate : 1, - detune : 1, - delay : 0, - seek : 0, - mute : false -} -mySound.play(config); -// ... -mySound.stop(); -``` +- [Navigation functions](api-nav.md) +- [Chat functions](api-chat.md) +- [Room functions](api-room.md) +- [UI functions](api-ui.md) +- [Sound functions](api-sound.md) +- [Controls functions](api-controls.md) + +- [List of deprecated functions](api-deprecated.md) diff --git a/docs/maps/api-room.md b/docs/maps/api-room.md new file mode 100644 index 00000000..dc7a8612 --- /dev/null +++ b/docs/maps/api-room.md @@ -0,0 +1,33 @@ +{.section-title.accent.text-primary} +# API Room functions Reference + +### Detecting when the user enters/leaves a zone + +``` +WA.room.onEnterZone(name: string, callback: () => void): void +WA.room.onLeaveZone(name: string, callback: () => void): void +``` + +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. + +
+
+ +
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. + +Example: + +```javascript +WA.room.onEnterZone('myZone', () => { + WA.chat.sendChatMessage("Hello!", 'Mr Robot'); +}) + +WA.room.onLeaveZone('myZone', () => { + WA.chat.sendChatMessage("Goodbye!", 'Mr Robot'); +}) +``` diff --git a/docs/maps/api-sound.md b/docs/maps/api-sound.md new file mode 100644 index 00000000..3c57ecae --- /dev/null +++ b/docs/maps/api-sound.md @@ -0,0 +1,34 @@ +{.section-title.accent.text-primary} +# API Sound functions Reference + +### Load a sound from an url + +``` +WA.sound.loadSound(url: string): Sound +``` + +Load a sound from an url + +Please note that `loadSound` returns an object of the `Sound` class + +The `Sound` class that represents a loaded sound contains two methods: `play(soundConfig : SoundConfig|undefined)` and `stop()` + +The parameter soundConfig is optional, if you call play without a Sound config the sound will be played with the basic configuration. + +Example: + +```javascript +var mySound = WA.sound.loadSound("Sound.ogg"); +var config = { + volume : 0.5, + loop : false, + rate : 1, + detune : 1, + delay : 0, + seek : 0, + mute : false +} +mySound.play(config); +// ... +mySound.stop(); +``` diff --git a/docs/maps/api-ui.md b/docs/maps/api-ui.md new file mode 100644 index 00000000..edda8613 --- /dev/null +++ b/docs/maps/api-ui.md @@ -0,0 +1,67 @@ +{.section-title.accent.text-primary} +# API Room functions Reference + +### Opening a popup + +In order to open a popup window, you must first define the position of the popup on your map. + +You can position this popup by using a "rectangle" object in Tiled that you will place on an "object" layer. + +
+
+ +
+
+ +
+
+ +``` +WA.ui.openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup +``` + +* **targetObject**: the name of the rectangle object defined in Tiled. +* **message**: the message to display in the popup. +* **buttons**: an array of action buttons defined underneath the popup. + +Action buttons are `ButtonDescriptor` objects containing these properties. + +* **label (_string_)**: The label of the button. +* **className (_string_)**: The visual type of the button. Can be one of "normal", "primary", "success", "warning", "error", "disabled". +* **callback (_(popup: Popup)=>void_)**: Callback called when the button is pressed. + +Please note that `openPopup` returns an object of the `Popup` class. Also, the callback called when a button is clicked is passed a `Popup` object. + +The `Popup` class that represents an open popup contains a single method: `close()`. This will obviously close the popup when called. + +```javascript +class Popup { + /** + * Closes the popup + */ + close() {}; +} +``` + +Example: + +```javascript +let helloWorldPopup; + +// Open the popup when we enter a given zone +helloWorldPopup = WA.room.onEnterZone('myZone', () => { + WA.ui.openPopup("popupRectangle", 'Hello world!', [{ + label: "Close", + className: "primary", + callback: (popup) => { + // Close the popup when the "Close" button is pressed. + popup.close(); + } + }); +}]); + +// Close the popup when we leave the zone. +WA.room.onLeaveZone('myZone', () => { + helloWorldPopup.close(); +}); +``` diff --git a/front/package.json b/front/package.json index 96213074..3c5e2b6a 100644 --- a/front/package.json +++ b/front/package.json @@ -41,6 +41,7 @@ "@types/simple-peer": "^9.6.0", "@types/socket.io-client": "^1.4.32", "axios": "^0.21.1", + "cross-env": "^7.0.3", "generic-type-guard": "^3.2.0", "google-protobuf": "^3.13.0", "nes.css": "^2.3.0", @@ -57,12 +58,12 @@ }, "scripts": { "start": "run-p serve svelte-check-watch", - "serve": "TS_NODE_PROJECT=\"tsconfig-for-webpack.json\" webpack serve --open", - "build": "TS_NODE_PROJECT=\"tsconfig-for-webpack.json\" NODE_ENV=production webpack", + "serve": "cross-env TS_NODE_PROJECT=\"tsconfig-for-webpack.json\" webpack serve --open", + "build": "cross-env TS_NODE_PROJECT=\"tsconfig-for-webpack.json\" NODE_ENV=production webpack", "test": "TS_NODE_PROJECT=\"tsconfig-for-jasmine.json\" ts-node node_modules/jasmine/bin/jasmine --config=jasmine.json", "lint": "node_modules/.bin/eslint src/ . --ext .ts", "fix": "node_modules/.bin/eslint --fix src/ . --ext .ts", "svelte-check-watch": "svelte-check --fail-on-warnings --fail-on-hints --compiler-warnings \"a11y-no-onchange:ignore,a11y-autofocus:ignore\" --watch", - "svelte-check": "svelte-check --fail-on-warnings --fail-on-hints --compiler-warnings \"a11y-no-onchange:ignore,a11y-autofocus:ignore\"" + "svelte-check": "svelte-check --fail-on-warnings --fail-on-hints --compiler-warnings \"a11y-no-onchange:ignore,a11y-autofocus:ignore\"" } } diff --git a/front/src/Api/Events/IframeEvent.ts b/front/src/Api/Events/IframeEvent.ts index 5991e663..e068cc22 100644 --- a/front/src/Api/Events/IframeEvent.ts +++ b/front/src/Api/Events/IframeEvent.ts @@ -36,7 +36,7 @@ export type IframeEventMap = { closeChatMessage : null loadSound: LoadSoundEvent playSound: PlaySoundEvent - stopSound: null + stopSound: null, } export interface IframeEvent { type: T; diff --git a/front/src/Api/iframe/IframeApiContribution.ts b/front/src/Api/iframe/IframeApiContribution.ts new file mode 100644 index 00000000..f3b25999 --- /dev/null +++ b/front/src/Api/iframe/IframeApiContribution.ts @@ -0,0 +1,31 @@ +import type * as tg from "generic-type-guard"; +import type { IframeEvent, IframeEventMap, IframeResponseEventMap } from '../Events/IframeEvent'; + +export function sendToWorkadventure(content: IframeEvent) { + window.parent.postMessage(content, "*") +} +type GuardedType> = Guard extends tg.TypeGuard ? T : never + +export interface IframeCallback> { + + typeChecker: Guard, + callback: (payloadData: T) => void +} + +export interface IframeCallbackContribution extends IframeCallback { + + type: Key +} + +/** + * !! be aware that the implemented attributes (addMethodsAtRoot and subObjectIdentifier) must be readonly + * + * + */ + +export abstract class IframeApiContribution>, +}> { + + abstract callbacks: T["callbacks"] +} diff --git a/front/src/Api/iframe/Sound/Sound.ts b/front/src/Api/iframe/Sound/Sound.ts new file mode 100644 index 00000000..3bb3251a --- /dev/null +++ b/front/src/Api/iframe/Sound/Sound.ts @@ -0,0 +1,39 @@ +import {sendToWorkadventure} from "../IframeApiContribution"; +import type {LoadSoundEvent} from "../../Events/LoadSoundEvent"; +import type {PlaySoundEvent} from "../../Events/PlaySoundEvent"; +import type {StopSoundEvent} from "../../Events/StopSoundEvent"; +import SoundConfig = Phaser.Types.Sound.SoundConfig; + +export class Sound { + constructor(private url: string) { + sendToWorkadventure({ + "type": 'loadSound', + "data": { + url: this.url, + } as LoadSoundEvent + + }); + } + + public play(config: SoundConfig) { + sendToWorkadventure({ + "type": 'playSound', + "data": { + url: this.url, + config + } as PlaySoundEvent + + }); + return this.url; + } + public stop() { + sendToWorkadventure({ + "type": 'stopSound', + "data": { + url: this.url, + } as StopSoundEvent + + }); + return this.url; + } +} diff --git a/front/src/Api/iframe/Ui/ButtonDescriptor.ts b/front/src/Api/iframe/Ui/ButtonDescriptor.ts new file mode 100644 index 00000000..119daf5c --- /dev/null +++ b/front/src/Api/iframe/Ui/ButtonDescriptor.ts @@ -0,0 +1,18 @@ +import type {Popup} from "./Popup"; + +export type ButtonClickedCallback = (popup: Popup) => void; + +export interface ButtonDescriptor { + /** + * The label of the button + */ + label: string, + /** + * The type of the button. Can be one of "normal", "primary", "success", "warning", "error", "disabled" + */ + className?: "normal" | "primary" | "success" | "warning" | "error" | "disabled", + /** + * Callback called if the button is pressed + */ + callback: ButtonClickedCallback, +} diff --git a/front/src/Api/iframe/Ui/Popup.ts b/front/src/Api/iframe/Ui/Popup.ts new file mode 100644 index 00000000..37dea922 --- /dev/null +++ b/front/src/Api/iframe/Ui/Popup.ts @@ -0,0 +1,19 @@ +import {sendToWorkadventure} from "../IframeApiContribution"; +import type {ClosePopupEvent} from "../../Events/ClosePopupEvent"; + +export class Popup { + constructor(private id: number) { + } + + /** + * Closes the popup + */ + public close(): void { + sendToWorkadventure({ + 'type': 'closePopup', + 'data': { + 'popupId': this.id, + } as ClosePopupEvent + }); + } +} diff --git a/front/src/Api/iframe/chat.ts b/front/src/Api/iframe/chat.ts new file mode 100644 index 00000000..5f73b744 --- /dev/null +++ b/front/src/Api/iframe/chat.ts @@ -0,0 +1,38 @@ +import type { ChatEvent } from '../Events/ChatEvent' +import { isUserInputChatEvent, UserInputChatEvent } from '../Events/UserInputChatEvent' +import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution' +import { apiCallback } from "./registeredCallbacks"; +import {Subject} from "rxjs"; + +const chatStream = new Subject(); + +class WorkadventureChatCommands extends IframeApiContribution { + + callbacks = [apiCallback({ + callback: (event: UserInputChatEvent) => { + chatStream.next(event.message); + }, + type: "userInputChat", + typeChecker: isUserInputChatEvent + })] + + + sendChatMessage(message: string, author: string) { + sendToWorkadventure({ + type: 'chat', + data: { + 'message': message, + 'author': author + } as ChatEvent + }) + } + + /** + * Listen to messages sent by the local user, in the chat. + */ + onChatMessage(callback: (message: string) => void) { + chatStream.subscribe(callback); + } +} + +export default new WorkadventureChatCommands() diff --git a/front/src/Api/iframe/controls.ts b/front/src/Api/iframe/controls.ts new file mode 100644 index 00000000..04e48990 --- /dev/null +++ b/front/src/Api/iframe/controls.ts @@ -0,0 +1,16 @@ +import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution'; + +class WorkadventureControlsCommands extends IframeApiContribution { + callbacks = [] + + disablePlayerControls(): void { + sendToWorkadventure({ 'type': 'disablePlayerControls', data: null }); + } + + restorePlayerControls(): void { + sendToWorkadventure({ 'type': 'restorePlayerControls', data: null }); + } +} + + +export default new WorkadventureControlsCommands(); diff --git a/front/src/Api/iframe/nav.ts b/front/src/Api/iframe/nav.ts new file mode 100644 index 00000000..b6798330 --- /dev/null +++ b/front/src/Api/iframe/nav.ts @@ -0,0 +1,56 @@ +import type { GoToPageEvent } from '../Events/GoToPageEvent'; +import type { OpenTabEvent } from '../Events/OpenTabEvent'; +import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution'; +import type {OpenCoWebSiteEvent} from "../Events/OpenCoWebSiteEvent"; + + +class WorkadventureNavigationCommands extends IframeApiContribution { + callbacks = [] + + + openTab(url: string): void { + sendToWorkadventure({ + "type": 'openTab', + "data": { + url + } as OpenTabEvent + }); + } + + goToPage(url: string): void { + sendToWorkadventure({ + "type": 'goToPage', + "data": { + url + } as GoToPageEvent + }); + } + + goToRoom(url: string): void { + sendToWorkadventure({ + "type": 'loadPage', + "data": { + url + } + }); + } + + openCoWebSite(url: string): void { + sendToWorkadventure({ + "type": 'openCoWebSite', + "data": { + url + } as OpenCoWebSiteEvent + }); + } + + closeCoWebSite(): void { + sendToWorkadventure({ + "type": 'closeCoWebSite', + data: null + }); + } +} + + +export default new WorkadventureNavigationCommands(); diff --git a/front/src/Api/iframe/registeredCallbacks.ts b/front/src/Api/iframe/registeredCallbacks.ts new file mode 100644 index 00000000..5d6f784d --- /dev/null +++ b/front/src/Api/iframe/registeredCallbacks.ts @@ -0,0 +1,16 @@ +import type {IframeResponseEventMap} from "../../Api/Events/IframeEvent"; +import type {IframeCallback} from "../../Api/iframe/IframeApiContribution"; +import type {IframeCallbackContribution} from "../../Api/iframe/IframeApiContribution"; + +export const registeredCallbacks: { [K in keyof IframeResponseEventMap]?: IframeCallback } = {} + +export function apiCallback(callbackData: IframeCallbackContribution): IframeCallbackContribution { + const iframeCallback = { + typeChecker: callbackData.typeChecker, + callback: callbackData.callback + } as IframeCallback; + + const newCallback = { [callbackData.type]: iframeCallback }; + Object.assign(registeredCallbacks, newCallback) + return callbackData as unknown as IframeCallbackContribution; +} diff --git a/front/src/Api/iframe/room.ts b/front/src/Api/iframe/room.ts new file mode 100644 index 00000000..ed412166 --- /dev/null +++ b/front/src/Api/iframe/room.ts @@ -0,0 +1,50 @@ +import { Subject } from "rxjs"; +import { EnterLeaveEvent, isEnterLeaveEvent } from '../Events/EnterLeaveEvent'; +import { IframeApiContribution } from './IframeApiContribution'; +import { apiCallback } from "./registeredCallbacks"; + +const enterStreams: Map> = new Map>(); +const leaveStreams: Map> = new Map>(); + +class WorkadventureRoomCommands extends IframeApiContribution { + callbacks = [ + apiCallback({ + callback: (payloadData: EnterLeaveEvent) => { + enterStreams.get(payloadData.name)?.next(); + }, + type: "enterEvent", + typeChecker: isEnterLeaveEvent + }), + apiCallback({ + type: "leaveEvent", + typeChecker: isEnterLeaveEvent, + callback: (payloadData) => { + leaveStreams.get(payloadData.name)?.next(); + } + }) + + ] + + + onEnterZone(name: string, callback: () => void): void { + let subject = enterStreams.get(name); + if (subject === undefined) { + subject = new Subject(); + enterStreams.set(name, subject); + } + subject.subscribe(callback); + + } + onLeaveZone(name: string, callback: () => void): void { + let subject = leaveStreams.get(name); + if (subject === undefined) { + subject = new Subject(); + leaveStreams.set(name, subject); + } + subject.subscribe(callback); + } + +} + + +export default new WorkadventureRoomCommands(); diff --git a/front/src/Api/iframe/sound.ts b/front/src/Api/iframe/sound.ts new file mode 100644 index 00000000..70430b46 --- /dev/null +++ b/front/src/Api/iframe/sound.ts @@ -0,0 +1,17 @@ +import type { LoadSoundEvent } from '../Events/LoadSoundEvent'; +import type { PlaySoundEvent } from '../Events/PlaySoundEvent'; +import type { StopSoundEvent } from '../Events/StopSoundEvent'; +import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution'; +import {Sound} from "./Sound/Sound"; + +class WorkadventureSoundCommands extends IframeApiContribution { + callbacks = [] + + loadSound(url: string): Sound { + return new Sound(url); + } + +} + + +export default new WorkadventureSoundCommands(); diff --git a/front/src/Api/iframe/ui.ts b/front/src/Api/iframe/ui.ts new file mode 100644 index 00000000..629d3c36 --- /dev/null +++ b/front/src/Api/iframe/ui.ts @@ -0,0 +1,83 @@ +import { isButtonClickedEvent } from '../Events/ButtonClickedEvent'; +import type { ClosePopupEvent } from '../Events/ClosePopupEvent'; +import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution'; +import { apiCallback } from "./registeredCallbacks"; +import {Popup} from "./Ui/Popup"; +import type {ButtonClickedCallback, ButtonDescriptor} from "./Ui/ButtonDescriptor"; + +let popupId = 0; +const popups: Map = new Map(); +const popupCallbacks: Map> = new Map>(); + +interface ZonedPopupOptions { + zone: string + objectLayerName?: string, + popupText: string, + delay?: number + popupOptions: Array +} + + +class WorkAdventureUiCommands extends IframeApiContribution { + + callbacks = [apiCallback({ + type: "buttonClickedEvent", + typeChecker: isButtonClickedEvent, + callback: (payloadData) => { + const callback = popupCallbacks.get(payloadData.popupId)?.get(payloadData.buttonId); + const popup = popups.get(payloadData.popupId); + if (popup === undefined) { + throw new Error('Could not find popup with ID "' + payloadData.popupId + '"'); + } + if (callback) { + callback(popup); + } + } + })]; + + + openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup { + popupId++; + const popup = new Popup(popupId); + const btnMap = new Map void>(); + popupCallbacks.set(popupId, btnMap); + let id = 0; + for (const button of buttons) { + const callback = button.callback; + if (callback) { + btnMap.set(id, () => { + callback(popup); + }); + } + id++; + } + + sendToWorkadventure({ + 'type': 'openPopup', + 'data': { + popupId, + targetObject, + message, + buttons: buttons.map((button) => { + return { + label: button.label, + className: button.className + }; + }) + } + }); + + popups.set(popupId, popup) + return popup; + } + + displayBubble(): void { + sendToWorkadventure({ 'type': 'displayBubble', data: null }); + } + + removeBubble(): void { + sendToWorkadventure({ 'type': 'removeBubble', data: null }); + } +} + +export default new WorkAdventureUiCommands(); diff --git a/front/src/Connexion/LocalUser.ts b/front/src/Connexion/LocalUser.ts index c877d119..d240a90e 100644 --- a/front/src/Connexion/LocalUser.ts +++ b/front/src/Connexion/LocalUser.ts @@ -10,7 +10,7 @@ export interface CharacterTexture { export const maxUserNameLength: number = MAX_USERNAME_LENGTH; export function isUserNameValid(value: unknown): boolean { - return typeof value === "string" && value.length > 0 && value.length <= maxUserNameLength && value.indexOf(' ') === -1; + return typeof value === "string" && value.length > 0 && value.length <= maxUserNameLength && /\S/.test(value); } export function areCharacterLayersValid(value: string[] | null): boolean { diff --git a/front/src/iframe_api.ts b/front/src/iframe_api.ts index 1b7887f3..ae5321cf 100644 --- a/front/src/iframe_api.ts +++ b/front/src/iframe_api.ts @@ -1,301 +1,175 @@ -import type { ChatEvent } from "./Api/Events/ChatEvent"; -import { isIframeResponseEventWrapper } from "./Api/Events/IframeEvent"; -import { isUserInputChatEvent, UserInputChatEvent } from "./Api/Events/UserInputChatEvent"; -import { Subject } from "rxjs"; -import { EnterLeaveEvent, isEnterLeaveEvent } from "./Api/Events/EnterLeaveEvent"; -import type { OpenPopupEvent } from "./Api/Events/OpenPopupEvent"; -import { isButtonClickedEvent } from "./Api/Events/ButtonClickedEvent"; -import type { ClosePopupEvent } from "./Api/Events/ClosePopupEvent"; -import type { OpenTabEvent } from "./Api/Events/OpenTabEvent"; -import type { GoToPageEvent } from "./Api/Events/GoToPageEvent"; -import type { OpenCoWebSiteEvent } from "./Api/Events/OpenCoWebSiteEvent"; -import type { PlaySoundEvent } from "./Api/Events/PlaySoundEvent"; -import type { StopSoundEvent } from "./Api/Events/StopSoundEvent"; -import type { LoadSoundEvent } from "./Api/Events/LoadSoundEvent"; -import SoundConfig = Phaser.Types.Sound.SoundConfig; -import type { LoadPageEvent } from './Api/Events/LoadPageEvent'; +import {registeredCallbacks} from "./Api/iframe/registeredCallbacks"; +import { + IframeResponseEvent, + IframeResponseEventMap, + isIframeResponseEventWrapper, + TypedMessageEvent +} from "./Api/Events/IframeEvent"; +import chat from "./Api/iframe/chat"; +import type {IframeCallback} from './Api/iframe/IframeApiContribution'; +import nav from "./Api/iframe/nav"; +import controls from "./Api/iframe/controls"; +import ui from "./Api/iframe/ui"; +import sound from "./Api/iframe/sound"; +import room from "./Api/iframe/room"; +import type {ButtonDescriptor} from "./Api/iframe/Ui/ButtonDescriptor"; +import type {Popup} from "./Api/iframe/Ui/Popup"; +import type {Sound} from "./Api/iframe/Sound/Sound"; -interface WorkAdventureApi { - sendChatMessage(message: string, author: string): void; - onChatMessage(callback: (message: string) => void): void; - closeChatMessage(): void; - onEnterZone(name: string, callback: () => void): void; - onLeaveZone(name: string, callback: () => void): void; - openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup; - openTab(url: string): void; - goToPage(url: string): void; - goToRoom(url: string): void; - openCoWebSite(url: string): void; - closeCoWebSite(): void; - disablePlayerControls(): void; - restorePlayerControls(): void; - displayBubble(): void; - removeBubble(): void; - loadSound(url : string): Sound; -} +const wa = { + ui, + nav, + controls, + chat, + sound, + room, + + // All methods below are deprecated and should not be used anymore. + // They are kept here for backward compatibility. + + /** + * @deprecated Use WA.chat.sendChatMessage instead + */ + sendChatMessage(message: string, author: string): void { + console.warn('Method WA.sendChatMessage is deprecated. Please use WA.chat.sendChatMessage instead'); + chat.sendChatMessage(message, author); + }, + /** + * @deprecated Use WA.chat.disablePlayerControls instead + */ + disablePlayerControls(): void { + console.warn('Method WA.disablePlayerControls is deprecated. Please use WA.controls.disablePlayerControls instead'); + controls.disablePlayerControls(); + }, + + /** + * @deprecated Use WA.controls.restorePlayerControls instead + */ + restorePlayerControls(): void { + console.warn('Method WA.restorePlayerControls is deprecated. Please use WA.controls.restorePlayerControls instead'); + controls.restorePlayerControls(); + }, + + /** + * @deprecated Use WA.ui.displayBubble instead + */ + displayBubble(): void { + console.warn('Method WA.displayBubble is deprecated. Please use WA.ui.displayBubble instead'); + ui.displayBubble(); + }, + + /** + * @deprecated Use WA.ui.removeBubble instead + */ + removeBubble(): void { + console.warn('Method WA.removeBubble is deprecated. Please use WA.ui.removeBubble instead'); + ui.removeBubble(); + }, + + /** + * @deprecated Use WA.nav.openTab instead + */ + openTab(url: string): void { + console.warn('Method WA.openTab is deprecated. Please use WA.nav.openTab instead'); + nav.openTab(url); + }, + + /** + * @deprecated Use WA.sound.loadSound instead + */ + loadSound(url: string) : Sound { + console.warn('Method WA.loadSound is deprecated. Please use WA.sound.loadSound instead'); + return sound.loadSound(url); + }, + + /** + * @deprecated Use WA.nav.goToPage instead + */ + goToPage(url : string) : void { + console.warn('Method WA.goToPage is deprecated. Please use WA.nav.goToPage instead'); + nav.goToPage(url); + }, + + /** + * @deprecated Use WA.nav.goToRoom instead + */ + goToRoom(url: string): void { + console.warn('Method WA.goToRoom is deprecated. Please use WA.nav.goToRoom instead'); + nav.goToRoom(url); + }, + + /** + * @deprecated Use WA.nav.openCoWebSite instead + */ + openCoWebSite(url : string) : void{ + console.warn('Method WA.openCoWebSite is deprecated. Please use WA.nav.openCoWebSite instead'); + nav.openCoWebSite(url); + }, + + /** + * @deprecated Use WA.nav.closeCoWebSite instead + */ + closeCoWebSite(): void { + console.warn('Method WA.closeCoWebSite is deprecated. Please use WA.nav.closeCoWebSite instead'); + nav.closeCoWebSite(); + }, + + /** + * @deprecated Use WA.controls.restorePlayerControls instead + */ + openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[]): Popup { + console.warn('Method WA.openPopup is deprecated. Please use WA.ui.openPopup instead'); + return ui.openPopup(targetObject, message, buttons); + }, + /** + * @deprecated Use WA.chat.onChatMessage instead + */ + onChatMessage(callback: (message: string) => void): void { + console.warn('Method WA.onChatMessage is deprecated. Please use WA.chat.onChatMessage instead'); + chat.onChatMessage(callback); + }, + /** + * @deprecated Use WA.room.onEnterZone instead + */ + onEnterZone(name: string, callback: () => void): void { + console.warn('Method WA.onEnterZone is deprecated. Please use WA.room.onEnterZone instead'); + room.onEnterZone(name, callback); + }, + /** + * @deprecated Use WA.room.onLeaveZone instead + */ + onLeaveZone(name: string, callback: () => void): void { + console.warn('Method WA.onLeaveZone is deprecated. Please use WA.room.onLeaveZone instead'); + room.onLeaveZone(name, callback); + }, +}; + +export type WorkAdventureApi = typeof wa; declare global { - // eslint-disable-next-line no-var - var WA: WorkAdventureApi + interface Window { + WA: WorkAdventureApi + } + let WA: WorkAdventureApi } -type ChatMessageCallback = (message: string) => void; -type ButtonClickedCallback = (popup: Popup) => void; +window.WA = wa; -const userInputChatStream: Subject = new Subject(); -const enterStreams: Map> = new Map>(); -const leaveStreams: Map> = new Map>(); -const popups: Map = new Map(); -const popupCallbacks: Map> = new Map>(); - -let popupId = 0; -interface ButtonDescriptor { - /** - * The label of the button - */ - label: string, - /** - * The type of the button. Can be one of "normal", "primary", "success", "warning", "error", "disabled" - */ - className?: "normal" | "primary" | "success" | "warning" | "error" | "disabled", - /** - * Callback called if the button is pressed - */ - callback: ButtonClickedCallback, -} - -class Popup { - inputValue: string; - - constructor(private id: number) { - this.inputValue = ''; - } - - /** - * Closes the popup - */ - public close(): void { - window.parent.postMessage({ - 'type': 'closePopup', - 'data': { - 'popupId': this.id, - 'inputValue': this.inputValue, - } as ClosePopupEvent - }, '*'); - } -} - -export class Sound { - constructor(private url: string) { - window.parent.postMessage({ - "type" : 'loadSound', - "data": { - url: this.url, - } as LoadSoundEvent - - },'*'); - } - - public play(config : SoundConfig) { - window.parent.postMessage({ - "type" : 'playSound', - "data": { - url: this.url, - config - } as PlaySoundEvent - - },'*'); - return this.url; - } - public stop() { - window.parent.postMessage({ - "type" : 'stopSound', - "data": { - url: this.url, - } as StopSoundEvent - - },'*'); - return this.url; - } - -} - -window.WA = { - /** - * Send a message in the chat. - * Only the local user will receive this message. - */ - sendChatMessage(message: string, author: string) { - window.parent.postMessage({ - 'type': 'chat', - 'data': { - 'message': message, - 'author': author - } as ChatEvent - }, '*'); - }, - closeChatMessage(): void { - window.parent.postMessage({ - "type": 'closeChatMessage' - }, '*'); - }, - disablePlayerControls(): void { - window.parent.postMessage({ 'type': 'disablePlayerControls' }, '*'); - }, - - restorePlayerControls(): void { - window.parent.postMessage({ 'type': 'restorePlayerControls' }, '*'); - }, - - displayBubble(): void { - window.parent.postMessage({ 'type': 'displayBubble' }, '*'); - }, - - removeBubble(): void { - window.parent.postMessage({ 'type': 'removeBubble' }, '*'); - }, - - openTab(url: string): void { - window.parent.postMessage({ - "type": 'openTab', - "data": { - url - } as OpenTabEvent - }, '*'); - }, - - loadSound(url: string) : Sound { - return new Sound(url); - }, - - goToPage(url : string) : void{ - window.parent.postMessage({ - "type": 'goToPage', - "data": { - url - } as GoToPageEvent - }, '*'); - }, - - goToRoom(url: string): void { - window.parent.postMessage({ - "type" : 'loadPage', - "data" : { - url - } as LoadPageEvent - },'*'); - }, - - openCoWebSite(url : string) : void{ - window.parent.postMessage({ - "type" : 'openCoWebSite', - "data" : { - url - } as OpenCoWebSiteEvent - }, '*'); - }, - - closeCoWebSite(): void { - window.parent.postMessage({ - "type": 'closeCoWebSite' - }, '*'); - }, - - openPopup(targetObject: string, message: string, buttons: ButtonDescriptor[], input: boolean = false): Popup { - popupId++; - const popup = new Popup(popupId); - const btnMap = new Map void>(); - popupCallbacks.set(popupId, btnMap); - let id = 0; - for (const button of buttons) { - const callback = button.callback; - if (callback) { - btnMap.set(id, () => { - callback(popup); - }); - } - id++; - } - - if(input) { - this.disablePlayerControls(); - } - - window.parent.postMessage({ - 'type': 'openPopup', - 'data': { - popupId, - targetObject, - message, - buttons: buttons.map((button) => { - return { - label: button.label, - className: button.className - }; - }), - input: input - } as OpenPopupEvent - }, '*'); - - popups.set(popupId, popup) - return popup; - }, - /** - * Listen to messages sent by the local user, in the chat. - */ - onChatMessage(callback: ChatMessageCallback): void { - userInputChatStream.subscribe((userInputChatEvent) => { - callback(userInputChatEvent.message); - }); - }, - onEnterZone(name: string, callback: () => void): void { - let subject = enterStreams.get(name); - if (subject === undefined) { - subject = new Subject(); - enterStreams.set(name, subject); - } - subject.subscribe(callback); - }, - onLeaveZone(name: string, callback: () => void): void { - let subject = leaveStreams.get(name); - if (subject === undefined) { - subject = new Subject(); - leaveStreams.set(name, subject); - } - subject.subscribe(callback); - }, -} - -window.addEventListener('message', message => { +window.addEventListener('message', (message: TypedMessageEvent>) => { if (message.source !== window.parent) { return; // Skip message in this event listener } - const payload = message.data; - console.debug(payload); if (isIframeResponseEventWrapper(payload)) { const payloadData = payload.data; - if (payload.type === 'userInputChat' && isUserInputChatEvent(payloadData)) { - userInputChatStream.next(payloadData); - } else if (payload.type === 'enterEvent' && isEnterLeaveEvent(payloadData)) { - enterStreams.get(payloadData.name)?.next(); - } else if (payload.type === 'leaveEvent' && isEnterLeaveEvent(payloadData)) { - leaveStreams.get(payloadData.name)?.next(); - } else if (payload.type === 'buttonClickedEvent' && isButtonClickedEvent(payloadData)) { - const callback = popupCallbacks.get(payloadData.popupId)?.get(payloadData.buttonId); - const popup = popups.get(payloadData.popupId); - if (popup === undefined) { - throw new Error('Could not find popup with ID "' + payloadData.popupId + '"'); - } - if(callback) { - popup.inputValue = payloadData.inputValue; - callback(popup); - } - } + const callback = registeredCallbacks[payload.type] as IframeCallback | undefined + if (callback?.typeChecker(payloadData)) { + callback?.callback(payloadData) + } } // ... diff --git a/maps/tests/deprecated_functions.json b/maps/tests/deprecated_functions.json new file mode 100644 index 00000000..a4c035f7 --- /dev/null +++ b/maps/tests/deprecated_functions.json @@ -0,0 +1,164 @@ +{ "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, 23, 23, 0, 0, 0, 0, 0, 0, 0, 0, 23, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":"triggerZone", + "opacity":1, + "properties":[ + { + "name":"zone", + "type":"string", + "value":"myTrigger" + }], + "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, 23, 23, 0, 0, 0, 0, 0, 0, 0, 0, 23, 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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":7, + "name":"popupZone", + "opacity":1, + "properties":[ + { + "name":"zone", + "type":"string", + "value":"popupZone" + }], + "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 + }, + { + "draworder":"topdown", + "id":3, + "name":"floorLayer", + "objects":[ + { + "height":147.135497146101, + "id":1, + "name":"myPopup2", + "rotation":0, + "type":"", + "visible":true, + "width":104.442827410047, + "x":142.817125079855, + "y":147.448134926559 + }, + { + "height":132.434722966794, + "id":2, + "name":"myPopup1", + "rotation":0, + "type":"", + "visible":true, + "width":125.735549178518, + "x":13.649632619596, + "y":50.8502491249093 + }, + { + "height":67, + "id":3, + "name":"", + "rotation":0, + "text": + { + "fontfamily":"Sans Serif", + "pixelsize":11, + "text":"Test:\nWalk on top carpet\nResult:\nA message \"Don't step on my carpet\" is displayed\nIn the console, deprecation warnings MUST be displayed", + "wrap":true + }, + "type":"", + "visible":true, + "width":252.4375, + "x":2.78125, + "y":2.5 + }, + { + "height":67, + "id":4, + "name":"", + "rotation":0, + "text": + { + "fontfamily":"Sans Serif", + "pixelsize":11, + "text":"Test:\nWalk on bottom carpet\nResult:\nA series of 2 popups open. A bubble opens. The player cannot move until popup closes.\nWhen the player leaves the zone, the bubble closes.\nIn the console, deprecation warnings MUST be displayed", + "wrap":true + }, + "type":"", + "visible":true, + "width":252.438, + "x":-1.71899999999999, + "y":163.5 + }], + "opacity":1, + "type":"objectgroup", + "visible":true, + "x":0, + "y":0 + }], + "nextlayerid":8, + "nextobjectid":5, + "orientation":"orthogonal", + "properties":[ + { + "name":"script", + "type":"string", + "value":"deprecated_script.js" + }], + "renderorder":"right-down", + "tiledversion":"2021.03.23", + "tileheight":32, + "tilesets":[ + { + "columns":11, + "firstgid":1, + "image":"tileset1.png", + "imageheight":352, + "imagewidth":352, + "margin":0, + "name":"tileset1", + "spacing":0, + "tilecount":121, + "tileheight":32, + "tilewidth":32 + }], + "tilewidth":32, + "type":"map", + "version":1.5, + "width":10 +} \ No newline at end of file diff --git a/maps/tests/deprecated_script.js b/maps/tests/deprecated_script.js new file mode 100644 index 00000000..4a889a8a --- /dev/null +++ b/maps/tests/deprecated_script.js @@ -0,0 +1,79 @@ +console.log('SCRIPT LAUNCHED'); +//WA.sendChatMessage('Hi, my name is Poly and I repeat what you say!', 'Poly Parrot'); +var isFirstTimeTuto = false; +var textFirstPopup = 'Hey ! This is how to open start a discussion with someone ! You can be 4 max in a booble'; +var textSecondPopup = 'You can also use the chat to communicate ! '; +var targetObjectTutoBubble ='myPopup1'; +var targetObjectTutoChat ='myPopup2'; +var popUpExplanation = undefined; +function launchTuto (){ + WA.openPopup(targetObjectTutoBubble, textFirstPopup, [ + { + label: "Next", + className: "popUpElement", + callback: (popup) => { + popup.close(); + + WA.openPopup(targetObjectTutoChat, textSecondPopup, [ + { + label: "Open Chat", + className: "popUpElement", + callback: (popup1) => { + WA.sendChatMessage("Hey you can talk here too ! ", 'WA Guide'); + popup1.close(); + WA.restorePlayerControls(); + } + } + + ]) + } + } + ]); + WA.disablePlayerControls(); + +} +WA.onChatMessage((message => { + console.log('CHAT MESSAGE RECEIVED BY SCRIPT'); + WA.sendChatMessage('Poly Parrot says: "'+message+'"', 'Poly Parrot'); +})); + +WA.onEnterZone('myTrigger', () => { + WA.sendChatMessage("Don't step on my carpet!", 'Poly Parrot'); +}) + +WA.onLeaveZone('popupZone', () => { +}) + +WA.onEnterZone('notExist', () => { + WA.sendChatMessage("YOU SHOULD NEVER SEE THIS", 'Poly Parrot'); +}) + +WA.onEnterZone('popupZone', () => { + WA.displayBubble(); + if (!isFirstTimeTuto) { + isFirstTimeTuto = true; + launchTuto(); + } + else popUpExplanation = WA.openPopup(targetObjectTutoChat,'Do you want to review the explanation ? ', [ + { + label: "No", + className: "popUpElementReviewexplanation", + callback: (popup) => { + popup.close(); + } + }, + { + label: "Yes", + className: "popUpElementReviewexplanation", + callback: (popup) => { + popup.close(); + launchTuto(); + } + } + ]) +}); + +WA.onLeaveZone('popupZone', () => { + if (popUpExplanation !== undefined) popUpExplanation.close(); + WA.removeBubble(); +}) diff --git a/maps/tests/index.html b/maps/tests/index.html index af70151e..2a27e78f 100644 --- a/maps/tests/index.html +++ b/maps/tests/index.html @@ -42,12 +42,12 @@ Testing scripting API with a script - + - Success Failure Pending + Success Failure Pending - Testing scripting API with a script + Testing goToPage script Api @@ -58,6 +58,14 @@ Testing scripting API loadSound() function + + + Success Failure Pending + + + Testing scripting API deprecated function + + Success Failure Pending diff --git a/maps/tests/script.js b/maps/tests/script.js index 4a889a8a..b300700f 100644 --- a/maps/tests/script.js +++ b/maps/tests/script.js @@ -7,21 +7,21 @@ var targetObjectTutoBubble ='myPopup1'; var targetObjectTutoChat ='myPopup2'; var popUpExplanation = undefined; function launchTuto (){ - WA.openPopup(targetObjectTutoBubble, textFirstPopup, [ + WA.ui.openPopup(targetObjectTutoBubble, textFirstPopup, [ { label: "Next", className: "popUpElement", callback: (popup) => { popup.close(); - WA.openPopup(targetObjectTutoChat, textSecondPopup, [ + WA.ui.openPopup(targetObjectTutoChat, textSecondPopup, [ { label: "Open Chat", className: "popUpElement", callback: (popup1) => { - WA.sendChatMessage("Hey you can talk here too ! ", 'WA Guide'); + WA.chat.sendChatMessage("Hey you can talk here too ! ", 'WA Guide'); popup1.close(); - WA.restorePlayerControls(); + WA.controls.restorePlayerControls(); } } @@ -29,32 +29,32 @@ function launchTuto (){ } } ]); - WA.disablePlayerControls(); + WA.controls.disablePlayerControls(); } -WA.onChatMessage((message => { +WA.chat.onChatMessage((message => { console.log('CHAT MESSAGE RECEIVED BY SCRIPT'); - WA.sendChatMessage('Poly Parrot says: "'+message+'"', 'Poly Parrot'); + WA.chat.sendChatMessage('Poly Parrot says: "'+message+'"', 'Poly Parrot'); })); -WA.onEnterZone('myTrigger', () => { - WA.sendChatMessage("Don't step on my carpet!", 'Poly Parrot'); +WA.room.onEnterZone('myTrigger', () => { + WA.chat.sendChatMessage("Don't step on my carpet!", 'Poly Parrot'); }) -WA.onLeaveZone('popupZone', () => { +WA.room.onLeaveZone('popupZone', () => { }) -WA.onEnterZone('notExist', () => { - WA.sendChatMessage("YOU SHOULD NEVER SEE THIS", 'Poly Parrot'); +WA.room.onEnterZone('notExist', () => { + WA.chat.sendChatMessage("YOU SHOULD NEVER SEE THIS", 'Poly Parrot'); }) -WA.onEnterZone('popupZone', () => { - WA.displayBubble(); +WA.room.onEnterZone('popupZone', () => { + WA.ui.displayBubble(); if (!isFirstTimeTuto) { isFirstTimeTuto = true; launchTuto(); } - else popUpExplanation = WA.openPopup(targetObjectTutoChat,'Do you want to review the explanation ? ', [ + else popUpExplanation = WA.ui.openPopup(targetObjectTutoChat,'Do you want to review the explanation ? ', [ { label: "No", className: "popUpElementReviewexplanation", @@ -73,7 +73,7 @@ WA.onEnterZone('popupZone', () => { ]) }); -WA.onLeaveZone('popupZone', () => { +WA.room.onLeaveZone('popupZone', () => { if (popUpExplanation !== undefined) popUpExplanation.close(); - WA.removeBubble(); + WA.ui.removeBubble(); }) diff --git a/website/.dockerignore b/website/.dockerignore deleted file mode 100644 index 576c21a2..00000000 --- a/website/.dockerignore +++ /dev/null @@ -1,5 +0,0 @@ -/dist/ -/node_modules/ -/dist/bundle.js -/yarn-error.log -/Dockerfile