From 2bef328d8a5fd1d27a9ced28315cf907ddc13f50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20N=C3=A9grier?= Date: Mon, 8 Mar 2021 18:57:59 +0100 Subject: [PATCH] Adding the abaility to track users entering/leaving a zone in the script language. --- front/src/Api/Events/EnterLeaveEvent.ts | 10 +++++++ front/src/Api/IframeListener.ts | 19 +++++++++++++ front/src/Phaser/Game/GameScene.ts | 8 ++++++ front/src/iframe_api.ts | 36 ++++++++++++++++++++++--- maps/tests/script.js | 12 +++++++++ maps/tests/script_api.json | 26 +++++++++++++++--- 6 files changed, 103 insertions(+), 8 deletions(-) create mode 100644 front/src/Api/Events/EnterLeaveEvent.ts diff --git a/front/src/Api/Events/EnterLeaveEvent.ts b/front/src/Api/Events/EnterLeaveEvent.ts new file mode 100644 index 00000000..0c0cb4ff --- /dev/null +++ b/front/src/Api/Events/EnterLeaveEvent.ts @@ -0,0 +1,10 @@ +import * as tg from "generic-type-guard"; + +export const isEnterLeaveEvent = + new tg.IsInterface().withProperties({ + name: tg.isString, + }).get(); +/** + * A message sent from the game to the iFrame when a user enters or leaves a zone marked with the "zone" property. + */ +export type EnterLeaveEvent = tg.GuardedType; diff --git a/front/src/Api/IframeListener.ts b/front/src/Api/IframeListener.ts index 66ea312f..3cd0d38c 100644 --- a/front/src/Api/IframeListener.ts +++ b/front/src/Api/IframeListener.ts @@ -4,6 +4,7 @@ import {IframeEvent, isIframeEventWrapper} from "./Events/IframeEvent"; import {UserInputChatEvent} from "./Events/UserInputChatEvent"; import * as crypto from "crypto"; import {HtmlUtils} from "../WebRtc/HtmlUtils"; +import {EnterLeaveEvent} from "./Events/EnterLeaveEvent"; @@ -130,6 +131,24 @@ class IframeListener { }); } + sendEnterEvent(name: string) { + this.postMessage({ + 'type': 'enterEvent', + 'data': { + "name": name + } as EnterLeaveEvent + }); + } + + sendLeaveEvent(name: string) { + this.postMessage({ + 'type': 'leaveEvent', + 'data': { + "name": name + } as EnterLeaveEvent + }); + } + /** * Sends the message... to all allowed iframes. */ diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index e9725db0..fa28650f 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -725,6 +725,14 @@ export class GameScene extends ResizableScene implements CenterListener { this.playAudio(newValue, true); }); + this.gameMap.onPropertyChange('zone', (newValue, oldValue) => { + if (newValue === undefined || newValue === false || newValue === '') { + iframeListener.sendLeaveEvent(oldValue as string); + } else { + iframeListener.sendEnterEvent(newValue as string); + } + }); + } private onMapExit(exitKey: string) { diff --git a/front/src/iframe_api.ts b/front/src/iframe_api.ts index 1a442661..7d334f85 100644 --- a/front/src/iframe_api.ts +++ b/front/src/iframe_api.ts @@ -2,10 +2,13 @@ import {ChatEvent, isChatEvent} from "./Api/Events/ChatEvent"; import {isIframeEventWrapper} from "./Api/Events/IframeEvent"; import {isUserInputChatEvent, UserInputChatEvent} from "./Api/Events/UserInputChatEvent"; import {Subject} from "rxjs"; +import {EnterLeaveEvent, isEnterLeaveEvent} from "./Api/Events/EnterLeaveEvent"; interface WorkAdventureApi { sendChatMessage(message: string, author: string): void; onChatMessage(callback: (message: string) => void): void; + onEnterZone(name: string, callback: () => void): void; + onLeaveZone(name: string, callback: () => void): void; } declare global { @@ -16,6 +19,8 @@ declare global { type ChatMessageCallback = (message: string) => void; const userInputChatStream: Subject = new Subject(); +const enterStreams: Map> = new Map>(); +const leaveStreams: Map> = new Map>(); window.WA = { @@ -39,19 +44,42 @@ window.WA = { 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 => { if (message.source !== window.parent) { - console.log('MESSAGE SKIPPED!!!') return; // Skip message in this event listener } const payload = message.data; + + console.log(payload); + if (isIframeEventWrapper(payload)) { - if (payload.type === 'userInputChat' && isUserInputChatEvent(payload.data)) { - userInputChatStream.next(payload.data); + 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(); } } diff --git a/maps/tests/script.js b/maps/tests/script.js index dd608bca..53d9ccb5 100644 --- a/maps/tests/script.js +++ b/maps/tests/script.js @@ -6,3 +6,15 @@ 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('myTrigger', () => { + WA.sendChatMessage("Thanks!", 'Poly Parrot'); +}) + +WA.onEnterZone('notExist', () => { + WA.sendChatMessage("YOU SHOULD NEVER SEE THIS", 'Poly Parrot'); +}) diff --git a/maps/tests/script_api.json b/maps/tests/script_api.json index d578f4be..ff1b0898 100644 --- a/maps/tests/script_api.json +++ b/maps/tests/script_api.json @@ -20,7 +20,25 @@ "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, 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, @@ -32,7 +50,7 @@ "width":10, "x":0, "y":0 - }, + }, { "draworder":"topdown", "id":3, @@ -44,7 +62,7 @@ "x":0, "y":0 }], - "nextlayerid":6, + "nextlayerid":7, "nextobjectid":1, "orientation":"orthogonal", "properties":[ @@ -74,4 +92,4 @@ "type":"map", "version":1.2, "width":10 -} \ No newline at end of file +}