From ba1bcf226abbd0bcc46419a328a87fe442cdb3dc Mon Sep 17 00:00:00 2001 From: jonny Date: Mon, 21 Jun 2021 18:22:31 +0200 Subject: [PATCH 1/3] menu command api --- docs/maps/api-ui.md | 23 ++ docs/maps/assets/menu-command.png | Bin 0 -> 9856 bytes front/src/Api/Events/IframeEvent.ts | 8 +- .../src/Api/Events/ui/MenuItemClickedEvent.ts | 21 + .../Api/Events/ui/MenuItemRegisterEvent.ts | 25 ++ front/src/Api/IframeListener.ts | 33 +- front/src/Api/iframe/ui.ts | 29 +- front/src/Phaser/Game/GameScene.ts | 387 +++++++++--------- front/src/Phaser/Menu/MenuScene.ts | 107 +++-- 9 files changed, 381 insertions(+), 252 deletions(-) create mode 100644 docs/maps/assets/menu-command.png create mode 100644 front/src/Api/Events/ui/MenuItemClickedEvent.ts create mode 100644 front/src/Api/Events/ui/MenuItemRegisterEvent.ts diff --git a/docs/maps/api-ui.md b/docs/maps/api-ui.md index edda8613..572de593 100644 --- a/docs/maps/api-ui.md +++ b/docs/maps/api-ui.md @@ -65,3 +65,26 @@ WA.room.onLeaveZone('myZone', () => { helloWorldPopup.close(); }); ``` + +### register additional menu entries + +adds an additional Entry to the main menu , these exist until the map is unloaded + + +```typescript +registerMenuCommand(menuCommand: string, callback: (menuCommand: string) => void): void +``` +Example: + + +```javascript + +WA.registerMenuCommand("test", () => { + WA.sendChatMessage("test clicked", "menu cmd") +}) + +``` + +
+ +
\ No newline at end of file diff --git a/docs/maps/assets/menu-command.png b/docs/maps/assets/menu-command.png new file mode 100644 index 0000000000000000000000000000000000000000..0caf75c9d772d63e43365040e033d3f93e69cf3c GIT binary patch literal 9856 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 Ho$&t!s2Avf literal 0 HcmV?d00001 diff --git a/front/src/Api/Events/IframeEvent.ts b/front/src/Api/Events/IframeEvent.ts index 4da8ea96..80fc5850 100644 --- a/front/src/Api/Events/IframeEvent.ts +++ b/front/src/Api/Events/IframeEvent.ts @@ -6,12 +6,14 @@ import type { ClosePopupEvent } from './ClosePopupEvent'; import type { EnterLeaveEvent } from './EnterLeaveEvent'; import type { GoToPageEvent } from './GoToPageEvent'; import type { LoadPageEvent } from './LoadPageEvent'; +import type { LoadSoundEvent } from "./LoadSoundEvent"; import type { OpenCoWebSiteEvent } from './OpenCoWebSiteEvent'; import type { OpenPopupEvent } from './OpenPopupEvent'; import type { OpenTabEvent } from './OpenTabEvent'; +import type { PlaySoundEvent } from "./PlaySoundEvent"; +import type { MenuItemClickedEvent } from './ui/MenuItemClickedEvent'; +import type { MenuItemRegisterEvent } from './ui/MenuItemRegisterEvent'; import type { UserInputChatEvent } from './UserInputChatEvent'; -import type { LoadSoundEvent} from "./LoadSoundEvent"; -import type {PlaySoundEvent} from "./PlaySoundEvent"; export interface TypedMessageEvent extends MessageEvent { @@ -36,6 +38,7 @@ export type IframeEventMap = { loadSound: LoadSoundEvent playSound: PlaySoundEvent stopSound: null, + registerMenuCommand: MenuItemRegisterEvent } export interface IframeEvent { type: T; @@ -52,6 +55,7 @@ export interface IframeResponseEventMap { leaveEvent: EnterLeaveEvent buttonClickedEvent: ButtonClickedEvent // gameState: GameStateEvent + menuItemClicked: MenuItemClickedEvent } export interface IframeResponseEvent { type: T; diff --git a/front/src/Api/Events/ui/MenuItemClickedEvent.ts b/front/src/Api/Events/ui/MenuItemClickedEvent.ts new file mode 100644 index 00000000..6444cb09 --- /dev/null +++ b/front/src/Api/Events/ui/MenuItemClickedEvent.ts @@ -0,0 +1,21 @@ +import * as tg from "generic-type-guard"; +import { iframeListener } from '../../IframeListener'; + +export const isMenuItemClickedEvent = + new tg.IsInterface().withProperties({ + menuItem: tg.isString + }).get(); +/** + * A message sent from the game to the iFrame when a menu item is clicked. + */ +export type MenuItemClickedEvent = tg.GuardedType; + + +export function sendMenuClickedEvent(menuItem: string) { + iframeListener.postMessage({ + 'type': 'menuItemClicked', + 'data': { + menuItem: menuItem, + } as MenuItemClickedEvent + }); +} \ No newline at end of file diff --git a/front/src/Api/Events/ui/MenuItemRegisterEvent.ts b/front/src/Api/Events/ui/MenuItemRegisterEvent.ts new file mode 100644 index 00000000..4a56d8a0 --- /dev/null +++ b/front/src/Api/Events/ui/MenuItemRegisterEvent.ts @@ -0,0 +1,25 @@ +import * as tg from "generic-type-guard"; +import { Subject } from 'rxjs'; + +export const isMenuItemRegisterEvent = + new tg.IsInterface().withProperties({ + menutItem: tg.isString + }).get(); +/** + * A message sent from the iFrame to the game to add a new menu item. + */ +export type MenuItemRegisterEvent = tg.GuardedType; + +export const isMenuItemRegisterIframeEvent = + new tg.IsInterface().withProperties({ + type: tg.isSingletonString("registerMenuCommand"), + data: isMenuItemRegisterEvent + }).get(); + + +const _registerMenuCommandStream: Subject = new Subject(); +export const registerMenuCommandStream = _registerMenuCommandStream.asObservable(); + +export function handleMenuItemRegistrationEvent(event: MenuItemRegisterEvent) { + _registerMenuCommandStream.next(event.menutItem) +} \ No newline at end of file diff --git a/front/src/Api/IframeListener.ts b/front/src/Api/IframeListener.ts index 232502a1..11852358 100644 --- a/front/src/Api/IframeListener.ts +++ b/front/src/Api/IframeListener.ts @@ -1,21 +1,22 @@ import { Subject } from "rxjs"; -import { ChatEvent, isChatEvent } from "./Events/ChatEvent"; import { HtmlUtils } from "../WebRtc/HtmlUtils"; +import type { ButtonClickedEvent } from "./Events/ButtonClickedEvent"; +import { ChatEvent, isChatEvent } from "./Events/ChatEvent"; +import { ClosePopupEvent, isClosePopupEvent } from "./Events/ClosePopupEvent"; import type { EnterLeaveEvent } from "./Events/EnterLeaveEvent"; +import { GoToPageEvent, isGoToPageEvent } from "./Events/GoToPageEvent"; +import { IframeEvent, IframeEventMap, IframeResponseEvent, IframeResponseEventMap, isIframeEventWrapper, TypedMessageEvent } from "./Events/IframeEvent"; +import { isLoadPageEvent } from './Events/LoadPageEvent'; +import { isLoadSoundEvent, LoadSoundEvent } from "./Events/LoadSoundEvent"; +import { isOpenCoWebsite, OpenCoWebSiteEvent } from "./Events/OpenCoWebSiteEvent"; import { isOpenPopupEvent, OpenPopupEvent } from "./Events/OpenPopupEvent"; import { isOpenTabEvent, OpenTabEvent } from "./Events/OpenTabEvent"; -import type { ButtonClickedEvent } from "./Events/ButtonClickedEvent"; -import { ClosePopupEvent, isClosePopupEvent } from "./Events/ClosePopupEvent"; -import { scriptUtils } from "./ScriptUtils"; -import { GoToPageEvent, isGoToPageEvent } from "./Events/GoToPageEvent"; -import { isOpenCoWebsite, OpenCoWebSiteEvent } from "./Events/OpenCoWebSiteEvent"; -import { IframeEventMap, IframeEvent, IframeResponseEvent, IframeResponseEventMap, isIframeEventWrapper, TypedMessageEvent } from "./Events/IframeEvent"; +import { isPlaySoundEvent, PlaySoundEvent } from "./Events/PlaySoundEvent"; +import { isStopSoundEvent, StopSoundEvent } from "./Events/StopSoundEvent"; +import { handleMenuItemRegistrationEvent, isMenuItemRegisterIframeEvent } from './Events/ui/MenuItemRegisterEvent'; import type { UserInputChatEvent } from "./Events/UserInputChatEvent"; -import { isLoadPageEvent } from './Events/LoadPageEvent'; -import {isPlaySoundEvent, PlaySoundEvent} from "./Events/PlaySoundEvent"; -import {isStopSoundEvent, StopSoundEvent} from "./Events/StopSoundEvent"; -import {isLoadSoundEvent, LoadSoundEvent} from "./Events/LoadSoundEvent"; +import { scriptUtils } from "./ScriptUtils"; /** * Listens to messages from iframes and turn those messages into easy to use observables. * Also allows to send messages to those iframes. @@ -33,7 +34,7 @@ class IframeListener { private readonly _goToPageStream: Subject = new Subject(); public readonly goToPageStream = this._goToPageStream.asObservable(); - + private readonly _loadPageStream: Subject = new Subject(); public readonly loadPageStream = this._loadPageStream.asObservable(); @@ -137,9 +138,11 @@ class IframeListener { } else if (payload.type === 'removeBubble') { this._removeBubbleStream.next(); - }else if (payload.type === 'loadPage' && isLoadPageEvent(payload.data)){ + } else if (payload.type === 'loadPage' && isLoadPageEvent(payload.data)) { this._loadPageStream.next(payload.data.url); - } + } else if (isMenuItemRegisterIframeEvent(payload)) [ + handleMenuItemRegistrationEvent(payload.data) + ] } @@ -263,7 +266,7 @@ class IframeListener { /** * Sends the message... to all allowed iframes. */ - private postMessage(message: IframeResponseEvent) { + public postMessage(message: IframeResponseEvent) { for (const iframe of this.iframes) { iframe.contentWindow?.postMessage(message, '*'); } diff --git a/front/src/Api/iframe/ui.ts b/front/src/Api/iframe/ui.ts index 629d3c36..8e9943b2 100644 --- a/front/src/Api/iframe/ui.ts +++ b/front/src/Api/iframe/ui.ts @@ -1,14 +1,17 @@ import { isButtonClickedEvent } from '../Events/ButtonClickedEvent'; -import type { ClosePopupEvent } from '../Events/ClosePopupEvent'; +import { isMenuItemClickedEvent } from '../Events/ui/MenuItemClickedEvent'; +import type { MenuItemRegisterEvent } from '../Events/ui/MenuItemRegisterEvent'; import { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution'; import { apiCallback } from "./registeredCallbacks"; -import {Popup} from "./Ui/Popup"; -import type {ButtonClickedCallback, ButtonDescriptor} from "./Ui/ButtonDescriptor"; +import type { ButtonClickedCallback, ButtonDescriptor } from "./Ui/ButtonDescriptor"; +import { Popup } from "./Ui/Popup"; let popupId = 0; const popups: Map = new Map(); const popupCallbacks: Map> = new Map>(); +const menuCallbacks: Map void> = new Map() + interface ZonedPopupOptions { zone: string objectLayerName?: string, @@ -33,6 +36,16 @@ class WorkAdventureUiCommands extends IframeApiContribution { + const callback = menuCallbacks.get(event.menuItem); + if (callback) { + callback(event.menuItem) + } + } })]; @@ -71,6 +84,16 @@ class WorkAdventureUiCommands extends IframeApiContribution void) { + menuCallbacks.set(commandDescriptor, callback); + sendToWorkadventure({ + 'type': 'registerMenuCommand', + 'data': { + menutItem: commandDescriptor + } as MenuItemRegisterEvent + }); + } + displayBubble(): void { sendToWorkadventure({ 'type': 'displayBubble', data: null }); } diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts index b5876d5a..317b27bb 100644 --- a/front/src/Phaser/Game/GameScene.ts +++ b/front/src/Phaser/Game/GameScene.ts @@ -1,4 +1,10 @@ -import {gameManager, HasMovedEvent} from "./GameManager"; +import { Queue } from 'queue-typescript'; +import type { Subscription } from "rxjs"; +import { ConsoleGlobalMessageManager } from "../../Administration/ConsoleGlobalMessageManager"; +import { GlobalMessageManager } from "../../Administration/GlobalMessageManager"; +import { userMessageManager } from "../../Administration/UserMessageManager"; +import { iframeListener } from "../../Api/IframeListener"; +import { connectionManager } from "../../Connexion/ConnectionManager"; import type { GroupCreatedUpdatedMessageInterface, MessageUserJoined, @@ -9,13 +15,50 @@ import type { PositionInterface, RoomJoinedMessageInterface } from "../../Connexion/ConnexionModels"; -import {hasMovedEventName, Player, requestEmoteEventName} from "../Player/Player"; +import { localUserStore } from "../../Connexion/LocalUserStore"; +import { Room } from "../../Connexion/Room"; +import type { RoomConnection } from "../../Connexion/RoomConnection"; +import { worldFullMessageStream } from "../../Connexion/WorldFullMessageStream"; import { DEBUG_MODE, JITSI_PRIVATE_MODE, MAX_PER_GROUP, - POSITION_DELAY, + POSITION_DELAY } from "../../Enum/EnvironmentVariable"; +import { TextureError } from "../../Exception/TextureError"; +import type { UserMovedMessage } from "../../Messages/generated/messages_pb"; +import { ProtobufClientUtils } from "../../Network/ProtobufClientUtils"; +import { peerStore } from "../../Stores/PeerStore"; +import { touchScreenManager } from "../../Touch/TouchScreenManager"; +import { urlManager } from "../../Url/UrlManager"; +import { audioManager } from "../../WebRtc/AudioManager"; +import { coWebsiteManager } from "../../WebRtc/CoWebsiteManager"; +import { HtmlUtils } from "../../WebRtc/HtmlUtils"; +import { jitsiFactory } from "../../WebRtc/JitsiFactory"; +import { + AUDIO_LOOP_PROPERTY, AUDIO_VOLUME_PROPERTY, CenterListener, + JITSI_MESSAGE_PROPERTIES, + layoutManager, + LayoutMode, + ON_ACTION_TRIGGER_BUTTON, + TRIGGER_JITSI_PROPERTIES, + TRIGGER_WEBSITE_PROPERTIES, + WEBSITE_MESSAGE_PROPERTIES +} from "../../WebRtc/LayoutManager"; +import { mediaManager } from "../../WebRtc/MediaManager"; +import { SimplePeer, UserSimplePeerInterface } from "../../WebRtc/SimplePeer"; +import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager"; +import { ChatModeIcon } from "../Components/ChatModeIcon"; +import { addLoader } from "../Components/Loader"; +import { joystickBaseImg, joystickBaseKey, joystickThumbImg, joystickThumbKey } from "../Components/MobileJoystick"; +import { OpenChatIcon, openChatIconName } from "../Components/OpenChatIcon"; +import { PresentationModeIcon } from "../Components/PresentationModeIcon"; +import { TextUtils } from "../Components/TextUtils"; +import { lazyLoadPlayerCharacterTextures, loadCustomTexture } from "../Entity/PlayerTexturesLoadingManager"; +import { RemotePlayer } from "../Entity/RemotePlayer"; +import type { ActionableItem } from "../Items/ActionableItem"; +import type { ItemFactoryInterface } from "../Items/ItemFactoryInterface"; +import { SelectCharacterScene, SelectCharacterSceneName } from "../Login/SelectCharacterScene"; import type { ITiledMap, ITiledMapLayer, @@ -24,80 +67,35 @@ import type { ITiledMapTileLayer, ITiledTileSet } from "../Map/ITiledMap"; -import type {AddPlayerInterface} from "./AddPlayerInterface"; -import {PlayerAnimationDirections} from "../Player/Animation"; -import {PlayerMovement} from "./PlayerMovement"; -import {PlayersPositionInterpolator} from "./PlayersPositionInterpolator"; -import {RemotePlayer} from "../Entity/RemotePlayer"; -import {Queue} from 'queue-typescript'; -import {SimplePeer, UserSimplePeerInterface} from "../../WebRtc/SimplePeer"; -import {ReconnectingSceneName} from "../Reconnecting/ReconnectingScene"; -import {lazyLoadPlayerCharacterTextures, loadCustomTexture} from "../Entity/PlayerTexturesLoadingManager"; -import { - CenterListener, - JITSI_MESSAGE_PROPERTIES, - layoutManager, - LayoutMode, - ON_ACTION_TRIGGER_BUTTON, - TRIGGER_JITSI_PROPERTIES, - TRIGGER_WEBSITE_PROPERTIES, - WEBSITE_MESSAGE_PROPERTIES, - AUDIO_VOLUME_PROPERTY, - AUDIO_LOOP_PROPERTY -} from "../../WebRtc/LayoutManager"; -import {GameMap} from "./GameMap"; -import {coWebsiteManager} from "../../WebRtc/CoWebsiteManager"; -import {mediaManager} from "../../WebRtc/MediaManager"; -import type {ItemFactoryInterface} from "../Items/ItemFactoryInterface"; -import type {ActionableItem} from "../Items/ActionableItem"; -import {UserInputManager} from "../UserInput/UserInputManager"; -import {soundManager} from "./SoundManager"; -import type {UserMovedMessage} from "../../Messages/generated/messages_pb"; -import {ProtobufClientUtils} from "../../Network/ProtobufClientUtils"; -import {connectionManager} from "../../Connexion/ConnectionManager"; -import type {RoomConnection} from "../../Connexion/RoomConnection"; -import {GlobalMessageManager} from "../../Administration/GlobalMessageManager"; -import {userMessageManager} from "../../Administration/UserMessageManager"; -import {ConsoleGlobalMessageManager} from "../../Administration/ConsoleGlobalMessageManager"; -import {ResizableScene} from "../Login/ResizableScene"; -import {Room} from "../../Connexion/Room"; -import {jitsiFactory} from "../../WebRtc/JitsiFactory"; -import {urlManager} from "../../Url/UrlManager"; -import {audioManager} from "../../WebRtc/AudioManager"; -import {PresentationModeIcon} from "../Components/PresentationModeIcon"; -import {ChatModeIcon} from "../Components/ChatModeIcon"; -import {OpenChatIcon, openChatIconName} from "../Components/OpenChatIcon"; -import {SelectCharacterScene, SelectCharacterSceneName} from "../Login/SelectCharacterScene"; -import {TextureError} from "../../Exception/TextureError"; -import {addLoader} from "../Components/Loader"; -import {ErrorSceneName} from "../Reconnecting/ErrorScene"; -import {localUserStore} from "../../Connexion/LocalUserStore"; -import {iframeListener} from "../../Api/IframeListener"; -import {HtmlUtils} from "../../WebRtc/HtmlUtils"; +import { MenuScene, MenuSceneName } from '../Menu/MenuScene'; +import { PlayerAnimationDirections } from "../Player/Animation"; +import { hasMovedEventName, Player, requestEmoteEventName } from "../Player/Player"; +import { ErrorSceneName } from "../Reconnecting/ErrorScene"; +import { ReconnectingSceneName } from "../Reconnecting/ReconnectingScene"; +import { waScaleManager } from "../Services/WaScaleManager"; +import { PinchManager } from "../UserInput/PinchManager"; +import { UserInputManager } from "../UserInput/UserInputManager"; +import type { AddPlayerInterface } from "./AddPlayerInterface"; +import { DEPTH_OVERLAY_INDEX } from "./DepthIndexes"; +import { DirtyScene } from "./DirtyScene"; +import { EmoteManager } from "./EmoteManager"; +import { gameManager, HasMovedEvent } from "./GameManager"; +import { GameMap } from "./GameMap"; +import { PlayerMovement } from "./PlayerMovement"; +import { PlayersPositionInterpolator } from "./PlayersPositionInterpolator"; +import { soundManager } from "./SoundManager"; import Texture = Phaser.Textures.Texture; import Sprite = Phaser.GameObjects.Sprite; import CanvasTexture = Phaser.Textures.CanvasTexture; import GameObject = Phaser.GameObjects.GameObject; import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR; import DOMElement = Phaser.GameObjects.DOMElement; -import EVENT_TYPE =Phaser.Scenes.Events -import type {Subscription} from "rxjs"; -import {worldFullMessageStream} from "../../Connexion/WorldFullMessageStream"; -import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager"; +import EVENT_TYPE = Phaser.Scenes.Events import RenderTexture = Phaser.GameObjects.RenderTexture; import Tilemap = Phaser.Tilemaps.Tilemap; -import {DirtyScene} from "./DirtyScene"; -import {TextUtils} from "../Components/TextUtils"; -import {touchScreenManager} from "../../Touch/TouchScreenManager"; -import {PinchManager} from "../UserInput/PinchManager"; -import {joystickBaseImg, joystickBaseKey, joystickThumbImg, joystickThumbKey} from "../Components/MobileJoystick"; -import {DEPTH_OVERLAY_INDEX} from "./DepthIndexes"; -import {waScaleManager} from "../Services/WaScaleManager"; -import {peerStore} from "../../Stores/PeerStore"; -import {EmoteManager} from "./EmoteManager"; export interface GameSceneInitInterface { - initPosition: PointInterface|null, + initPosition: PointInterface | null, reconnecting: boolean } @@ -134,10 +132,10 @@ interface DeleteGroupEventInterface { const defaultStartLayerName = 'start'; export class GameScene extends DirtyScene implements CenterListener { - Terrains : Array; + Terrains: Array; CurrentPlayer!: Player; MapPlayers!: Phaser.Physics.Arcade.Group; - MapPlayersByKey : Map = new Map(); + MapPlayersByKey: Map = new Map(); Map!: Phaser.Tilemaps.Tilemap; Layers!: Array; Objects!: Array; @@ -147,10 +145,10 @@ export class GameScene extends DirtyScene implements CenterListener { startY!: number; circleTexture!: CanvasTexture; circleRedTexture!: CanvasTexture; - pendingEvents: Queue = new Queue(); - private initPosition: PositionInterface|null = null; + pendingEvents: Queue = new Queue(); + private initPosition: PositionInterface | null = null; private playersPositionInterpolator = new PlayersPositionInterpolator(); - public connection: RoomConnection|undefined; + public connection: RoomConnection | undefined; private simplePeer!: SimplePeer; private GlobalMessageManager!: GlobalMessageManager; public ConsoleGlobalMessageManager!: ConsoleGlobalMessageManager; @@ -159,7 +157,7 @@ export class GameScene extends DirtyScene implements CenterListener { // A promise that will resolve when the "create" method is called (signaling loading is ended) private createPromise: Promise; private createPromiseResolve!: (value?: void | PromiseLike) => void; - private iframeSubscriptionList! : Array; + private iframeSubscriptionList!: Array; private peerStoreUnsubscribe!: () => void; MapUrlFile: string; RoomId: string; @@ -179,22 +177,22 @@ export class GameScene extends DirtyScene implements CenterListener { private gameMap!: GameMap; private actionableItems: Map = new Map(); // The item that can be selected by pressing the space key. - private outlinedItem: ActionableItem|null = null; + private outlinedItem: ActionableItem | null = null; public userInputManager!: UserInputManager; - private isReconnecting: boolean|undefined = undefined; + private isReconnecting: boolean | undefined = undefined; private startLayerName!: string | null; private openChatIcon!: OpenChatIcon; private playerName!: string; private characterLayers!: string[]; - private companion!: string|null; - private messageSubscription: Subscription|null = null; - private popUpElements : Map = new Map(); - private originalMapUrl: string|undefined; - private pinchManager: PinchManager|undefined; + private companion!: string | null; + private messageSubscription: Subscription | null = null; + private popUpElements: Map = new Map(); + private originalMapUrl: string | undefined; + private pinchManager: PinchManager | undefined; private mapTransitioning: boolean = false; //used to prevent transitions happenning at the same time. private emoteManager!: EmoteManager; - constructor(private room: Room, MapUrlFile: string, customKey?: string|undefined) { + constructor(private room: Room, MapUrlFile: string, customKey?: string | undefined) { super({ key: customKey ?? room.id }); @@ -234,13 +232,13 @@ export class GameScene extends DirtyScene implements CenterListener { //this.load.audio('audio-report-message', '/resources/objects/report-message.mp3'); this.sound.pauseOnBlur = false; - this.load.on(FILE_LOAD_ERROR, (file: {src: string}) => { + this.load.on(FILE_LOAD_ERROR, (file: { src: string }) => { // If we happen to be in HTTP and we are trying to load a URL in HTTPS only... (this happens only in dev environments) if (window.location.protocol === 'http:' && file.src === this.MapUrlFile && file.src.startsWith('http:') && this.originalMapUrl === undefined) { this.originalMapUrl = this.MapUrlFile; this.MapUrlFile = this.MapUrlFile.replace('http://', 'https://'); this.load.tilemapTiledJSON(this.MapUrlFile, this.MapUrlFile); - this.load.on('filecomplete-tilemapJSON-'+this.MapUrlFile, (key: string, type: string, data: unknown) => { + this.load.on('filecomplete-tilemapJSON-' + this.MapUrlFile, (key: string, type: string, data: unknown) => { this.onMapLoad(data); }); return; @@ -254,7 +252,7 @@ export class GameScene extends DirtyScene implements CenterListener { this.originalMapUrl = this.MapUrlFile; this.MapUrlFile = this.MapUrlFile.replace('https://', 'http://'); this.load.tilemapTiledJSON(this.MapUrlFile, this.MapUrlFile); - this.load.on('filecomplete-tilemapJSON-'+this.MapUrlFile, (key: string, type: string, data: unknown) => { + this.load.on('filecomplete-tilemapJSON-' + this.MapUrlFile, (key: string, type: string, data: unknown) => { this.onMapLoad(data); }); return; @@ -266,7 +264,7 @@ export class GameScene extends DirtyScene implements CenterListener { message: this.originalMapUrl ?? file.src }); }); - this.load.on('filecomplete-tilemapJSON-'+this.MapUrlFile, (key: string, type: string, data: unknown) => { + this.load.on('filecomplete-tilemapJSON-' + this.MapUrlFile, (key: string, type: string, data: unknown) => { this.onMapLoad(data); }); //TODO strategy to add access token @@ -278,7 +276,7 @@ export class GameScene extends DirtyScene implements CenterListener { this.onMapLoad(data); } - this.load.spritesheet('layout_modes', 'resources/objects/layout_modes.png', {frameWidth: 32, frameHeight: 32}); + this.load.spritesheet('layout_modes', 'resources/objects/layout_modes.png', { frameWidth: 32, frameHeight: 32 }); this.load.bitmapFont('main_font', 'resources/fonts/arcade.png', 'resources/fonts/arcade.xml'); //eslint-disable-next-line @typescript-eslint/no-explicit-any (this.load as any).rexWebFont({ @@ -315,7 +313,7 @@ export class GameScene extends DirtyScene implements CenterListener { for (const layer of this.mapFile.layers) { if (layer.type === 'objectgroup') { for (const object of layer.objects) { - let objectsOfType: ITiledMapObject[]|undefined; + let objectsOfType: ITiledMapObject[] | undefined; if (!objects.has(object.type)) { objectsOfType = new Array(); } else { @@ -343,7 +341,7 @@ export class GameScene extends DirtyScene implements CenterListener { } default: continue; - //throw new Error('Unsupported object type: "'+ itemType +'"'); + //throw new Error('Unsupported object type: "'+ itemType +'"'); } itemFactory.preload(this.load); @@ -378,7 +376,7 @@ export class GameScene extends DirtyScene implements CenterListener { } //hook initialisation - init(initData : GameSceneInitInterface) { + init(initData: GameSceneInitInterface) { if (initData.initPosition !== undefined) { this.initPosition = initData.initPosition; //todo: still used? } @@ -457,7 +455,7 @@ export class GameScene extends DirtyScene implements CenterListener { this.Objects = new Array(); //initialise list of other player - this.MapPlayers = this.physics.add.group({immovable: true}); + this.MapPlayers = this.physics.add.group({ immovable: true }); //create input to move @@ -563,7 +561,7 @@ export class GameScene extends DirtyScene implements CenterListener { bottom: camera.scrollY + camera.height, }, this.companion - ).then((onConnect: OnConnectInterface) => { + ).then((onConnect: OnConnectInterface) => { this.connection = onConnect.connection; this.connection.onUserJoins((message: MessageUserJoined) => { @@ -716,23 +714,23 @@ export class GameScene extends DirtyScene implements CenterListener { const contextRed = this.circleRedTexture.context; contextRed.beginPath(); contextRed.arc(48, 48, 48, 0, 2 * Math.PI, false); - //context.lineWidth = 5; + //context.lineWidth = 5; contextRed.strokeStyle = '#ff0000'; contextRed.stroke(); this.circleRedTexture.refresh(); } - private safeParseJSONstring(jsonString: string|undefined, propertyName: string) { + private safeParseJSONstring(jsonString: string | undefined, propertyName: string) { try { return jsonString ? JSON.parse(jsonString) : {}; - } catch(e) { + } catch (e) { console.warn('Invalid JSON found in property "' + propertyName + '" of the map:' + jsonString, e); return {} } } - private triggerOnMapLayerPropertyChange(){ + private triggerOnMapLayerPropertyChange() { this.gameMap.onPropertyChange('exitSceneUrl', (newValue, oldValue) => { if (newValue) this.onMapExit(newValue as string); }); @@ -743,22 +741,22 @@ export class GameScene extends DirtyScene implements CenterListener { if (newValue === undefined) { layoutManager.removeActionButton('openWebsite', this.userInputManager); coWebsiteManager.closeCoWebsite(); - }else{ + } else { const openWebsiteFunction = () => { coWebsiteManager.loadCoWebsite(newValue as string, this.MapUrlFile, allProps.get('openWebsiteAllowApi') as boolean | undefined, allProps.get('openWebsitePolicy') as string | undefined); layoutManager.removeActionButton('openWebsite', this.userInputManager); }; const openWebsiteTriggerValue = allProps.get(TRIGGER_WEBSITE_PROPERTIES); - if(openWebsiteTriggerValue && openWebsiteTriggerValue === ON_ACTION_TRIGGER_BUTTON) { + if (openWebsiteTriggerValue && openWebsiteTriggerValue === ON_ACTION_TRIGGER_BUTTON) { let message = allProps.get(WEBSITE_MESSAGE_PROPERTIES); - if(message === undefined){ + if (message === undefined) { message = 'Press SPACE or touch here to open web site'; } layoutManager.addActionButton('openWebsite', message.toString(), () => { openWebsiteFunction(); }, this.userInputManager); - }else{ + } else { openWebsiteFunction(); } } @@ -767,12 +765,12 @@ export class GameScene extends DirtyScene implements CenterListener { if (newValue === undefined) { layoutManager.removeActionButton('jitsiRoom', this.userInputManager); this.stopJitsi(); - }else{ + } else { const openJitsiRoomFunction = () => { const roomName = jitsiFactory.getRoomName(newValue.toString(), this.instance); - const jitsiUrl = allProps.get("jitsiUrl") as string|undefined; + const jitsiUrl = allProps.get("jitsiUrl") as string | undefined; if (JITSI_PRIVATE_MODE && !jitsiUrl) { - const adminTag = allProps.get("jitsiRoomAdminTag") as string|undefined; + const adminTag = allProps.get("jitsiRoomAdminTag") as string | undefined; this.connection?.emitQueryJitsiJwtMessage(roomName, adminTag); } else { @@ -782,7 +780,7 @@ export class GameScene extends DirtyScene implements CenterListener { } const jitsiTriggerValue = allProps.get(TRIGGER_JITSI_PROPERTIES); - if(jitsiTriggerValue && jitsiTriggerValue === ON_ACTION_TRIGGER_BUTTON) { + if (jitsiTriggerValue && jitsiTriggerValue === ON_ACTION_TRIGGER_BUTTON) { let message = allProps.get(JITSI_MESSAGE_PROPERTIES); if (message === undefined) { message = 'Press SPACE or touch here to enter Jitsi Meet room'; @@ -790,7 +788,7 @@ export class GameScene extends DirtyScene implements CenterListener { layoutManager.addActionButton('jitsiRoom', message.toString(), () => { openJitsiRoomFunction(); }, this.userInputManager); - }else{ + } else { openJitsiRoomFunction(); } } @@ -803,8 +801,8 @@ export class GameScene extends DirtyScene implements CenterListener { } }); this.gameMap.onPropertyChange('playAudio', (newValue, oldValue, allProps) => { - const volume = allProps.get(AUDIO_VOLUME_PROPERTY) as number|undefined; - const loop = allProps.get(AUDIO_LOOP_PROPERTY) as boolean|undefined; + const volume = allProps.get(AUDIO_VOLUME_PROPERTY) as number | undefined; + const loop = allProps.get(AUDIO_LOOP_PROPERTY) as boolean | undefined; newValue === undefined ? audioManager.unloadAudio() : audioManager.playAudio(newValue, this.getMapDirUrl(), volume, loop); }); // TODO: This legacy property should be removed at some point @@ -823,13 +821,13 @@ export class GameScene extends DirtyScene implements CenterListener { } private listenToIframeEvents(): void { - this.iframeSubscriptionList = []; - this.iframeSubscriptionList.push(iframeListener.openPopupStream.subscribe((openPopupEvent) => { + this.iframeSubscriptionList = []; + this.iframeSubscriptionList.push(iframeListener.openPopupStream.subscribe((openPopupEvent) => { - let objectLayerSquare : ITiledMapObject; + let objectLayerSquare: ITiledMapObject; const targetObjectData = this.getObjectLayerData(openPopupEvent.targetObject); - if (targetObjectData !== undefined){ - objectLayerSquare = targetObjectData; + if (targetObjectData !== undefined) { + objectLayerSquare = targetObjectData; } else { console.error("Error while opening a popup. Cannot find an object on the map with name '" + openPopupEvent.targetObject + "'. The first parameter of WA.openPopup() must be the name of a rectangle object in your map."); return; @@ -842,14 +840,14 @@ ${escapedMessage} html += buttonContainer; let id = 0; for (const button of openPopupEvent.buttons) { - html += ``; + html += ``; id++; } html += ''; - const domElement = this.add.dom(objectLayerSquare.x , + const domElement = this.add.dom(objectLayerSquare.x, objectLayerSquare.y).createFromHTML(html); - const container : HTMLDivElement = domElement.getChildByID("container") as HTMLDivElement; + const container: HTMLDivElement = domElement.getChildByID("container") as HTMLDivElement; container.style.width = objectLayerSquare.width + "px"; domElement.scale = 0; domElement.setClassName('popUpElement'); @@ -869,73 +867,70 @@ ${escapedMessage} id++; } this.tweens.add({ - targets : domElement , - scale : 1, - ease : "EaseOut", - duration : 400, + targets: domElement, + scale: 1, + ease: "EaseOut", + duration: 400, }); this.popUpElements.set(openPopupEvent.popupId, domElement); })); - this.iframeSubscriptionList.push(iframeListener.closePopupStream.subscribe((closePopupEvent) => { + this.iframeSubscriptionList.push(iframeListener.closePopupStream.subscribe((closePopupEvent) => { const popUpElement = this.popUpElements.get(closePopupEvent.popupId); if (popUpElement === undefined) { - console.error('Could not close popup with ID ', closePopupEvent.popupId,'. Maybe it has already been closed?'); + console.error('Could not close popup with ID ', closePopupEvent.popupId, '. Maybe it has already been closed?'); } this.tweens.add({ - targets : popUpElement , - scale : 0, - ease : "EaseOut", - duration : 400, - onComplete : () => { + targets: popUpElement, + scale: 0, + ease: "EaseOut", + duration: 400, + onComplete: () => { popUpElement?.destroy(); this.popUpElements.delete(closePopupEvent.popupId); }, }); })); - this.iframeSubscriptionList.push(iframeListener.disablePlayerControlStream.subscribe(()=>{ + this.iframeSubscriptionList.push(iframeListener.disablePlayerControlStream.subscribe(() => { this.userInputManager.disableControls(); })); - this.iframeSubscriptionList.push(iframeListener.playSoundStream.subscribe((playSoundEvent)=> - { - const url = new URL(playSoundEvent.url, this.MapUrlFile); - soundManager.playSound(this.load,this.sound,url.toString(),playSoundEvent.config); - })) + this.iframeSubscriptionList.push(iframeListener.playSoundStream.subscribe((playSoundEvent) => { + const url = new URL(playSoundEvent.url, this.MapUrlFile); + soundManager.playSound(this.load, this.sound, url.toString(), playSoundEvent.config); + })) - this.iframeSubscriptionList.push(iframeListener.stopSoundStream.subscribe((stopSoundEvent)=> - { + this.iframeSubscriptionList.push(iframeListener.stopSoundStream.subscribe((stopSoundEvent) => { const url = new URL(stopSoundEvent.url, this.MapUrlFile); - soundManager.stopSound(this.sound,url.toString()); + soundManager.stopSound(this.sound, url.toString()); })) - this.iframeSubscriptionList.push(iframeListener.loadSoundStream.subscribe((loadSoundEvent)=> - { + this.iframeSubscriptionList.push(iframeListener.loadSoundStream.subscribe((loadSoundEvent) => { const url = new URL(loadSoundEvent.url, this.MapUrlFile); - soundManager.loadSound(this.load,this.sound,url.toString()); + soundManager.loadSound(this.load, this.sound, url.toString()); })) - this.iframeSubscriptionList.push(iframeListener.enablePlayerControlStream.subscribe(()=>{ + this.iframeSubscriptionList.push(iframeListener.enablePlayerControlStream.subscribe(() => { this.userInputManager.restoreControls(); })); - this.iframeSubscriptionList.push(iframeListener.loadPageStream.subscribe((url:string)=>{ - this.loadNextGame(url).then(()=>{ - this.events.once(EVENT_TYPE.POST_UPDATE,()=>{ + this.iframeSubscriptionList.push(iframeListener.loadPageStream.subscribe((url: string) => { + this.loadNextGame(url).then(() => { + this.events.once(EVENT_TYPE.POST_UPDATE, () => { this.onMapExit(url); }) }) })); - let scriptedBubbleSprite : Sprite; - this.iframeSubscriptionList.push(iframeListener.displayBubbleStream.subscribe(()=>{ - scriptedBubbleSprite = new Sprite(this,this.CurrentPlayer.x + 25,this.CurrentPlayer.y,'circleSprite-white'); + let scriptedBubbleSprite: Sprite; + this.iframeSubscriptionList.push(iframeListener.displayBubbleStream.subscribe(() => { + scriptedBubbleSprite = new Sprite(this, this.CurrentPlayer.x + 25, this.CurrentPlayer.y, 'circleSprite-white'); scriptedBubbleSprite.setDisplayOrigin(48, 48); this.add.existing(scriptedBubbleSprite); })); - this.iframeSubscriptionList.push(iframeListener.removeBubbleStream.subscribe(()=>{ + this.iframeSubscriptionList.push(iframeListener.removeBubbleStream.subscribe(() => { scriptedBubbleSprite.destroy(); })); @@ -948,9 +943,11 @@ ${escapedMessage} private onMapExit(exitKey: string) { if (this.mapTransitioning) return; this.mapTransitioning = true; - const {roomId, hash} = Room.getIdFromIdentifier(exitKey, this.MapUrlFile, this.instance); - if (!roomId) throw new Error('Could not find the room from its exit key: '+exitKey); + const { roomId, hash } = Room.getIdFromIdentifier(exitKey, this.MapUrlFile, this.instance); + if (!roomId) throw new Error('Could not find the room from its exit key: ' + exitKey); urlManager.pushStartLayerNameToUrl(hash); + const menuScene: MenuScene = this.scene.get(MenuSceneName) as MenuScene + menuScene.reset() if (roomId !== this.scene.key) { if (this.scene.get(roomId) === null) { console.error("next room not loaded", exitKey); @@ -992,7 +989,7 @@ ${escapedMessage} mediaManager.hideGameOverlay(); - for(const iframeEvents of this.iframeSubscriptionList){ + for (const iframeEvents of this.iframeSubscriptionList) { iframeEvents.unsubscribe(); } } @@ -1012,7 +1009,7 @@ ${escapedMessage} private switchLayoutMode(): void { //if discussion is activated, this layout cannot be activated - if(mediaManager.activatedDiscussion){ + if (mediaManager.activatedDiscussion) { return; } const mode = layoutManager.getLayoutMode(); @@ -1053,24 +1050,24 @@ ${escapedMessage} private initPositionFromLayerName(layerName: string) { for (const layer of this.gameMap.layersIterator) { - if ((layerName === layer.name || layer.name.endsWith('/'+layerName)) && layer.type === 'tilelayer' && (layerName === defaultStartLayerName || this.isStartLayer(layer))) { + if ((layerName === layer.name || layer.name.endsWith('/' + layerName)) && layer.type === 'tilelayer' && (layerName === defaultStartLayerName || this.isStartLayer(layer))) { const startPosition = this.startUser(layer); - this.startX = startPosition.x + this.mapFile.tilewidth/2; - this.startY = startPosition.y + this.mapFile.tileheight/2; + this.startX = startPosition.x + this.mapFile.tilewidth / 2; + this.startY = startPosition.y + this.mapFile.tileheight / 2; } } } - private getExitUrl(layer: ITiledMapLayer): string|undefined { - return this.getProperty(layer, "exitUrl") as string|undefined; + private getExitUrl(layer: ITiledMapLayer): string | undefined { + return this.getProperty(layer, "exitUrl") as string | undefined; } /** * @deprecated the map property exitSceneUrl is deprecated */ - private getExitSceneUrl(layer: ITiledMapLayer): string|undefined { - return this.getProperty(layer, "exitSceneUrl") as string|undefined; + private getExitSceneUrl(layer: ITiledMapLayer): string | undefined { + return this.getProperty(layer, "exitSceneUrl") as string | undefined; } private isStartLayer(layer: ITiledMapLayer): boolean { @@ -1081,8 +1078,8 @@ ${escapedMessage} return (this.getProperties(map, "script") as string[]).map((script) => (new URL(script, this.MapUrlFile)).toString()); } - private getProperty(layer: ITiledMapLayer|ITiledMap, name: string): string|boolean|number|undefined { - const properties: ITiledMapLayerProperty[]|undefined = layer.properties; + private getProperty(layer: ITiledMapLayer | ITiledMap, name: string): string | boolean | number | undefined { + const properties: ITiledMapLayerProperty[] | undefined = layer.properties; if (!properties) { return undefined; } @@ -1093,8 +1090,8 @@ ${escapedMessage} return obj.value; } - private getProperties(layer: ITiledMapLayer|ITiledMap, name: string): (string|number|boolean|undefined)[] { - const properties: ITiledMapLayerProperty[]|undefined = layer.properties; + private getProperties(layer: ITiledMapLayer | ITiledMap, name: string): (string | number | boolean | undefined)[] { + const properties: ITiledMapLayerProperty[] | undefined = layer.properties; if (!properties) { return []; } @@ -1103,29 +1100,29 @@ ${escapedMessage} //todo: push that into the gameManager private loadNextGame(exitSceneIdentifier: string): Promise { - const {roomId, hash} = Room.getIdFromIdentifier(exitSceneIdentifier, this.MapUrlFile, this.instance); + const { roomId, hash } = Room.getIdFromIdentifier(exitSceneIdentifier, this.MapUrlFile, this.instance); const room = new Room(roomId); - return gameManager.loadMap(room, this.scene).catch(() => {}); + return gameManager.loadMap(room, this.scene).catch(() => { }); } private startUser(layer: ITiledMapTileLayer): PositionInterface { const tiles = layer.data; - if (typeof(tiles) === 'string') { + if (typeof (tiles) === 'string') { throw new Error('The content of a JSON map must be filled as a JSON array, not as a string'); } - const possibleStartPositions : PositionInterface[] = []; - tiles.forEach((objectKey : number, key: number) => { - if(objectKey === 0){ + const possibleStartPositions: PositionInterface[] = []; + tiles.forEach((objectKey: number, key: number) => { + if (objectKey === 0) { return; } const y = Math.floor(key / layer.width); const x = key % layer.width; - possibleStartPositions.push({x: x * this.mapFile.tilewidth, y: y * this.mapFile.tilewidth}); + possibleStartPositions.push({ x: x * this.mapFile.tilewidth, y: y * this.mapFile.tilewidth }); }); // Get a value at random amongst allowed values if (possibleStartPositions.length === 0) { - console.warn('The start layer "'+layer.name+'" for this map is empty.'); + console.warn('The start layer "' + layer.name + '" for this map is empty.'); return { x: 0, y: 0 @@ -1137,12 +1134,12 @@ ${escapedMessage} //todo: in a dedicated class/function? initCamera() { - this.cameras.main.setBounds(0,0, this.Map.widthInPixels, this.Map.heightInPixels); + this.cameras.main.setBounds(0, 0, this.Map.widthInPixels, this.Map.heightInPixels); this.cameras.main.startFollow(this.CurrentPlayer, true); this.updateCameraOffset(); } - addLayer(Layer : Phaser.Tilemaps.TilemapLayer){ + addLayer(Layer: Phaser.Tilemaps.TilemapLayer) { this.Layers.push(Layer); } @@ -1152,7 +1149,7 @@ ${escapedMessage} this.physics.add.collider(this.CurrentPlayer, Layer, (object1: GameObject, object2: GameObject) => { //this.CurrentPlayer.say("Collision with layer : "+ (object2 as Tile).layer.name) }); - Layer.setCollisionByProperty({collides: true}); + Layer.setCollisionByProperty({ collides: true }); if (DEBUG_MODE) { //debug code to see the collision hitbox of the object in the top layer Layer.renderDebug(this.add.graphics(), { @@ -1164,7 +1161,7 @@ ${escapedMessage} }); } - createCurrentPlayer(){ + createCurrentPlayer() { //TODO create animation moving between exit and start const texturesPromise = lazyLoadPlayerCharacterTextures(this.load, this.characterLayers); try { @@ -1189,8 +1186,8 @@ ${escapedMessage} this.CurrentPlayer.on(requestEmoteEventName, (emoteKey: string) => { this.connection?.emitEmoteEvent(emoteKey); }) - }catch (err){ - if(err instanceof TextureError) { + } catch (err) { + if (err instanceof TextureError) { gameManager.leaveGame(this, SelectCharacterSceneName, new SelectCharacterScene()); } throw err; @@ -1251,7 +1248,7 @@ ${escapedMessage} } let shortestDistance: number = Infinity; - let selectedItem: ActionableItem|null = null; + let selectedItem: ActionableItem | null = null; for (const item of this.actionableItems.values()) { const distance = item.actionableDistance(x, y); if (distance !== null && distance < shortestDistance) { @@ -1285,7 +1282,7 @@ ${escapedMessage} * @param time * @param delta The delta time in ms since the last frame. This is a smoothed and capped value based on the FPS rate. */ - update(time: number, delta: number) : void { + update(time: number, delta: number): void { this.dirty = false; mediaManager.updateScene(); this.currentTick = time; @@ -1345,8 +1342,8 @@ ${escapedMessage} const currentPlayerId = this.connection?.getUserId(); this.removeAllRemotePlayers(); // load map - usersPosition.forEach((userPosition : MessageUserPositionInterface) => { - if(userPosition.userId === currentPlayerId){ + usersPosition.forEach((userPosition: MessageUserPositionInterface) => { + if (userPosition.userId === currentPlayerId) { return; } this.addPlayer(userPosition); @@ -1356,16 +1353,16 @@ ${escapedMessage} /** * Called by the connexion when a new player arrives on a map */ - public addPlayer(addPlayerData : AddPlayerInterface) : void { + public addPlayer(addPlayerData: AddPlayerInterface): void { this.pendingEvents.enqueue({ type: "AddPlayerEvent", event: addPlayerData }); } - private doAddPlayer(addPlayerData : AddPlayerInterface): void { + private doAddPlayer(addPlayerData: AddPlayerInterface): void { //check if exist player, if exist, move position - if(this.MapPlayersByKey.has(addPlayerData.userId)){ + if (this.MapPlayersByKey.has(addPlayerData.userId)) { this.updatePlayerPosition({ userId: addPlayerData.userId, position: addPlayerData.position @@ -1427,10 +1424,10 @@ ${escapedMessage} } private doUpdatePlayerPosition(message: MessageUserMovedInterface): void { - const player : RemotePlayer | undefined = this.MapPlayersByKey.get(message.userId); + const player: RemotePlayer | undefined = this.MapPlayersByKey.get(message.userId); if (player === undefined) { //throw new Error('Cannot find player with ID "' + message.userId +'"'); - console.error('Cannot update position of player with ID "' + message.userId +'": player not found'); + console.error('Cannot update position of player with ID "' + message.userId + '": player not found'); return; } @@ -1474,7 +1471,7 @@ ${escapedMessage} doDeleteGroup(groupId: number): void { const group = this.groups.get(groupId); - if(!group){ + if (!group) { return; } group.destroy(); @@ -1503,7 +1500,7 @@ ${escapedMessage} bottom: camera.scrollY + camera.height, }); } - private getObjectLayerData(objectName : string) : ITiledMapObject| undefined{ + private getObjectLayerData(objectName: string): ITiledMapObject | undefined { for (const layer of this.mapFile.layers) { if (layer.type === 'objectgroup' && layer.name === 'floorLayer') { for (const object of layer.objects) { @@ -1537,7 +1534,7 @@ ${escapedMessage} const game = HtmlUtils.querySelectorOrFail('#game canvas'); // Let's put this in Game coordinates by applying the zoom level: - this.cameras.main.setFollowOffset((xCenter - game.offsetWidth/2) * window.devicePixelRatio / this.scale.zoom , (yCenter - game.offsetHeight/2) * window.devicePixelRatio / this.scale.zoom); + this.cameras.main.setFollowOffset((xCenter - game.offsetWidth / 2) * window.devicePixelRatio / this.scale.zoom, (yCenter - game.offsetHeight / 2) * window.devicePixelRatio / this.scale.zoom); } public onCenterChange(): void { @@ -1546,16 +1543,16 @@ ${escapedMessage} public startJitsi(roomName: string, jwt?: string): void { const allProps = this.gameMap.getCurrentProperties(); - const jitsiConfig = this.safeParseJSONstring(allProps.get("jitsiConfig") as string|undefined, 'jitsiConfig'); - const jitsiInterfaceConfig = this.safeParseJSONstring(allProps.get("jitsiInterfaceConfig") as string|undefined, 'jitsiInterfaceConfig'); - const jitsiUrl = allProps.get("jitsiUrl") as string|undefined; + const jitsiConfig = this.safeParseJSONstring(allProps.get("jitsiConfig") as string | undefined, 'jitsiConfig'); + const jitsiInterfaceConfig = this.safeParseJSONstring(allProps.get("jitsiInterfaceConfig") as string | undefined, 'jitsiInterfaceConfig'); + const jitsiUrl = allProps.get("jitsiUrl") as string | undefined; jitsiFactory.start(roomName, this.playerName, jwt, jitsiConfig, jitsiInterfaceConfig, jitsiUrl); this.connection?.setSilent(true); mediaManager.hideGameOverlay(); //permit to stop jitsi when user close iframe - mediaManager.addTriggerCloseJitsiFrameButton('close-jisi',() => { + mediaManager.addTriggerCloseJitsiFrameButton('close-jisi', () => { this.stopJitsi(); }); } @@ -1569,7 +1566,7 @@ ${escapedMessage} } //todo: put this into an 'orchestrator' scene (EntryScene?) - private bannedUser(){ + private bannedUser() { this.cleanupClosingScene(); this.userInputManager.disableControls(); this.scene.start(ErrorSceneName, { @@ -1580,22 +1577,22 @@ ${escapedMessage} } //todo: put this into an 'orchestrator' scene (EntryScene?) - private showWorldFullError(message: string|null): void { + private showWorldFullError(message: string | null): void { this.cleanupClosingScene(); this.scene.stop(ReconnectingSceneName); this.scene.remove(ReconnectingSceneName); this.userInputManager.disableControls(); //FIX ME to use status code - if(message == undefined){ + if (message == undefined) { this.scene.start(ErrorSceneName, { title: 'Connection rejected', subTitle: 'The world you are trying to join is full. Try again later.', message: 'If you want more information, you may contact us at: workadventure@thecodingmachine.com' }); - }else{ + } else { this.scene.start(ErrorSceneName, { title: 'Connection rejected', - subTitle: 'You cannot join the World. Try again later. \n\r \n\r Error: '+message+'.', + subTitle: 'You cannot join the World. Try again later. \n\r \n\r Error: ' + message + '.', message: 'If you want more information, you may contact administrator or contact us at: workadventure@thecodingmachine.com' }); } diff --git a/front/src/Phaser/Menu/MenuScene.ts b/front/src/Phaser/Menu/MenuScene.ts index 54fa395a..d5d0387a 100644 --- a/front/src/Phaser/Menu/MenuScene.ts +++ b/front/src/Phaser/Menu/MenuScene.ts @@ -1,16 +1,20 @@ -import {LoginScene, LoginSceneName} from "../Login/LoginScene"; -import {SelectCharacterScene, SelectCharacterSceneName} from "../Login/SelectCharacterScene"; -import {SelectCompanionScene, SelectCompanionSceneName} from "../Login/SelectCompanionScene"; -import {gameManager} from "../Game/GameManager"; -import {localUserStore} from "../../Connexion/LocalUserStore"; -import {mediaManager} from "../../WebRtc/MediaManager"; -import {gameReportKey, gameReportRessource, ReportMenu} from "./ReportMenu"; -import {connectionManager} from "../../Connexion/ConnectionManager"; -import {GameConnexionTypes} from "../../Url/UrlManager"; -import {WarningContainer, warningContainerHtml, warningContainerKey} from "../Components/WarningContainer"; -import {worldFullWarningStream} from "../../Connexion/WorldFullWarningStream"; -import {menuIconVisible} from "../../Stores/MenuStore"; -import {videoConstraintStore} from "../../Stores/MediaStore"; +import { Subscription } from 'rxjs'; +import { sendMenuClickedEvent } from '../../Api/Events/ui/MenuItemClickedEvent'; +import { registerMenuCommandStream } from '../../Api/Events/ui/MenuItemRegisterEvent'; +import { connectionManager } from "../../Connexion/ConnectionManager"; +import { localUserStore } from "../../Connexion/LocalUserStore"; +import { worldFullWarningStream } from "../../Connexion/WorldFullWarningStream"; +import { videoConstraintStore } from "../../Stores/MediaStore"; +import { menuIconVisible } from "../../Stores/MenuStore"; +import { GameConnexionTypes } from "../../Url/UrlManager"; +import { HtmlUtils } from '../../WebRtc/HtmlUtils'; +import { mediaManager } from "../../WebRtc/MediaManager"; +import { WarningContainer, warningContainerHtml, warningContainerKey } from "../Components/WarningContainer"; +import { gameManager } from "../Game/GameManager"; +import { LoginScene, LoginSceneName } from "../Login/LoginScene"; +import { SelectCharacterScene, SelectCharacterSceneName } from "../Login/SelectCharacterScene"; +import { SelectCompanionScene, SelectCompanionSceneName } from "../Login/SelectCompanionScene"; +import { gameReportKey, gameReportRessource, ReportMenu } from "./ReportMenu"; export const MenuSceneName = 'MenuScene'; const gameMenuKey = 'gameMenu'; @@ -37,15 +41,38 @@ export class MenuScene extends Phaser.Scene { private menuButton!: Phaser.GameObjects.DOMElement; private warningContainer: WarningContainer | null = null; private warningContainerTimeout: NodeJS.Timeout | null = null; - + private subscriptions = new Subscription() constructor() { - super({key: MenuSceneName}); + super({ key: MenuSceneName }); this.gameQualityValue = localUserStore.getGameQualityValue(); this.videoQualityValue = localUserStore.getVideoQualityValue(); + + this.subscriptions.add(registerMenuCommandStream.subscribe(menuCommand => { + this.addMenuOption(menuCommand); + + })) } - preload () { + reset() { + const addedMenuItems = [...this.menuElement.node.querySelectorAll(".fromApi")]; + for (let index = addedMenuItems.length - 1; index >= 0; index--) { + addedMenuItems[index].remove() + } + } + + public addMenuOption(menuText: string) { + const wrappingSection = document.createElement("section") + const escapedHtml = HtmlUtils.escapeHtml(menuText); + wrappingSection.innerHTML = `` + const menuItemContainer = this.menuElement.node.querySelector("#gameMenu main"); + if (menuItemContainer) { + menuItemContainer.querySelector(`#${escapedHtml}.fromApi`)?.remove() + menuItemContainer.insertBefore(wrappingSection, menuItemContainer.querySelector("#socialLinks")) + } + } + + preload() { this.load.html(gameMenuKey, 'resources/html/gameMenu.html'); this.load.html(gameMenuIconKey, 'resources/html/gameMenuIcon.html'); this.load.html(gameSettingsMenuKey, 'resources/html/gameQualityMenu.html'); @@ -68,11 +95,11 @@ export class MenuScene extends Phaser.Scene { this.gameShareElement = this.add.dom(middleX, -400).createFromCache(gameShare); MenuScene.revealMenusAfterInit(this.gameShareElement, gameShare); this.gameShareElement.addListener('click'); - this.gameShareElement.on('click', (event:MouseEvent) => { + this.gameShareElement.on('click', (event: MouseEvent) => { event.preventDefault(); - if((event?.target as HTMLInputElement).id === 'gameShareFormSubmit') { + if ((event?.target as HTMLInputElement).id === 'gameShareFormSubmit') { this.copyLink(); - }else if((event?.target as HTMLInputElement).id === 'gameShareFormCancel') { + } else if ((event?.target as HTMLInputElement).id === 'gameShareFormCancel') { this.closeGameShare(); } }); @@ -128,8 +155,8 @@ export class MenuScene extends Phaser.Scene { } //TODO bind with future metadata of card //if (connectionManager.getConnexionType === GameConnexionTypes.anonymous){ - const adminSection = this.menuElement.getChildByID('socialLinks') as HTMLElement; - adminSection.hidden = false; + const adminSection = this.menuElement.getChildByID('socialLinks') as HTMLElement; + adminSection.hidden = false; //} this.tweens.add({ targets: this.menuElement, @@ -179,28 +206,28 @@ export class MenuScene extends Phaser.Scene { this.settingsMenuOpened = true; const gameQualitySelect = this.gameQualityMenuElement.getChildByID('select-game-quality') as HTMLInputElement; - gameQualitySelect.value = ''+this.gameQualityValue; + gameQualitySelect.value = '' + this.gameQualityValue; const videoQualitySelect = this.gameQualityMenuElement.getChildByID('select-video-quality') as HTMLInputElement; - videoQualitySelect.value = ''+this.videoQualityValue; + videoQualitySelect.value = '' + this.videoQualityValue; this.gameQualityMenuElement.addListener('click'); - this.gameQualityMenuElement.on('click', (event:MouseEvent) => { + this.gameQualityMenuElement.on('click', (event: MouseEvent) => { event.preventDefault(); if ((event?.target as HTMLInputElement).id === 'gameQualityFormSubmit') { const gameQualitySelect = this.gameQualityMenuElement.getChildByID('select-game-quality') as HTMLInputElement; const videoQualitySelect = this.gameQualityMenuElement.getChildByID('select-video-quality') as HTMLInputElement; this.saveSetting(parseInt(gameQualitySelect.value), parseInt(videoQualitySelect.value)); - } else if((event?.target as HTMLInputElement).id === 'gameQualityFormCancel') { + } else if ((event?.target as HTMLInputElement).id === 'gameQualityFormCancel') { this.closeGameQualityMenu(); } }); - let middleY = this.scale.height / 2 - 392/2; - if(middleY < 0){ + let middleY = this.scale.height / 2 - 392 / 2; + if (middleY < 0) { middleY = 0; } - let middleX = this.scale.width / 2 - 457/2; - if(middleX < 0){ + let middleX = this.scale.width / 2 - 457 / 2; + if (middleX < 0) { middleX = 0; } this.tweens.add({ @@ -226,7 +253,7 @@ export class MenuScene extends Phaser.Scene { } - private openGameShare(): void{ + private openGameShare(): void { if (this.gameShareOpened) { this.closeGameShare(); return; @@ -240,11 +267,11 @@ export class MenuScene extends Phaser.Scene { this.gameShareOpened = true; let middleY = this.scale.height / 2 - 85; - if(middleY < 0){ + if (middleY < 0) { middleY = 0; } let middleX = this.scale.width / 2 - 200; - if(middleX < 0){ + if (middleX < 0) { middleX = 0; } this.tweens.add({ @@ -256,7 +283,7 @@ export class MenuScene extends Phaser.Scene { }); } - private closeGameShare(): void{ + private closeGameShare(): void { const gameShareInfo = this.gameShareElement.getChildByID('gameShareInfo') as HTMLParagraphElement; gameShareInfo.innerText = ''; gameShareInfo.style.display = 'none'; @@ -269,12 +296,18 @@ export class MenuScene extends Phaser.Scene { }); } - private onMenuClick(event:MouseEvent) { - if((event?.target as HTMLInputElement).classList.contains('not-button')){ + private onMenuClick(event: MouseEvent) { + const htmlMenuItem = (event?.target as HTMLInputElement); + if (htmlMenuItem.classList.contains('not-button')) { return; } event.preventDefault(); + if (htmlMenuItem.classList.contains("fromApi")) { + sendMenuClickedEvent(htmlMenuItem.id) + return + } + switch ((event?.target as HTMLInputElement).id) { case 'changeNameButton': this.closeSideMenu(); @@ -316,7 +349,7 @@ export class MenuScene extends Phaser.Scene { gameShareInfo.style.display = 'block'; } - private saveSetting(valueGame: number, valueVideo: number){ + private saveSetting(valueGame: number, valueVideo: number) { if (valueGame !== this.gameQualityValue) { this.gameQualityValue = valueGame; localUserStore.setGameQualityValue(valueGame); @@ -337,7 +370,7 @@ export class MenuScene extends Phaser.Scene { window.open(sparkHost, '_blank'); } - private closeAll(){ + private closeAll() { this.closeGameQualityMenu(); this.closeGameShare(); this.gameReportElement.close(); From 64a00481f031699b3a5d9ef37fcd038294a80737 Mon Sep 17 00:00:00 2001 From: jonny Date: Mon, 21 Jun 2021 18:39:02 +0200 Subject: [PATCH 2/3] fixed wrong import --- .../src/Api/Events/ui/MenuItemClickedEvent.ts | 9 -------- front/src/Api/iframe/Ui/MenuItem.ts | 11 +++++++++ front/src/Phaser/Menu/MenuScene.ts | 2 +- front/src/iframe_api.ts | 23 ++++++++++--------- 4 files changed, 24 insertions(+), 21 deletions(-) create mode 100644 front/src/Api/iframe/Ui/MenuItem.ts diff --git a/front/src/Api/Events/ui/MenuItemClickedEvent.ts b/front/src/Api/Events/ui/MenuItemClickedEvent.ts index 6444cb09..fad2944f 100644 --- a/front/src/Api/Events/ui/MenuItemClickedEvent.ts +++ b/front/src/Api/Events/ui/MenuItemClickedEvent.ts @@ -1,5 +1,4 @@ import * as tg from "generic-type-guard"; -import { iframeListener } from '../../IframeListener'; export const isMenuItemClickedEvent = new tg.IsInterface().withProperties({ @@ -11,11 +10,3 @@ export const isMenuItemClickedEvent = export type MenuItemClickedEvent = tg.GuardedType; -export function sendMenuClickedEvent(menuItem: string) { - iframeListener.postMessage({ - 'type': 'menuItemClicked', - 'data': { - menuItem: menuItem, - } as MenuItemClickedEvent - }); -} \ No newline at end of file diff --git a/front/src/Api/iframe/Ui/MenuItem.ts b/front/src/Api/iframe/Ui/MenuItem.ts new file mode 100644 index 00000000..9782ea7a --- /dev/null +++ b/front/src/Api/iframe/Ui/MenuItem.ts @@ -0,0 +1,11 @@ +import type { MenuItemClickedEvent } from '../../Events/ui/MenuItemClickedEvent'; +import { iframeListener } from '../../IframeListener'; + +export function sendMenuClickedEvent(menuItem: string) { + iframeListener.postMessage({ + 'type': 'menuItemClicked', + 'data': { + menuItem: menuItem, + } as MenuItemClickedEvent + }); +} \ No newline at end of file diff --git a/front/src/Phaser/Menu/MenuScene.ts b/front/src/Phaser/Menu/MenuScene.ts index d5d0387a..e405a758 100644 --- a/front/src/Phaser/Menu/MenuScene.ts +++ b/front/src/Phaser/Menu/MenuScene.ts @@ -1,6 +1,6 @@ import { Subscription } from 'rxjs'; -import { sendMenuClickedEvent } from '../../Api/Events/ui/MenuItemClickedEvent'; import { registerMenuCommandStream } from '../../Api/Events/ui/MenuItemRegisterEvent'; +import { sendMenuClickedEvent } from '../../Api/iframe/Ui/MenuItem'; import { connectionManager } from "../../Connexion/ConnectionManager"; import { localUserStore } from "../../Connexion/LocalUserStore"; import { worldFullWarningStream } from "../../Connexion/WorldFullWarningStream"; diff --git a/front/src/iframe_api.ts b/front/src/iframe_api.ts index ae5321cf..874f0ace 100644 --- a/front/src/iframe_api.ts +++ b/front/src/iframe_api.ts @@ -1,4 +1,3 @@ -import {registeredCallbacks} from "./Api/iframe/registeredCallbacks"; import { IframeResponseEvent, IframeResponseEventMap, @@ -6,15 +5,17 @@ import { 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 type { IframeCallback } from './Api/iframe/IframeApiContribution'; +import nav from "./Api/iframe/nav"; +import { registeredCallbacks } from "./Api/iframe/registeredCallbacks"; 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"; +import sound from "./Api/iframe/sound"; +import type { Sound } from "./Api/iframe/Sound/Sound"; +import ui from "./Api/iframe/ui"; +import type { ButtonDescriptor } from "./Api/iframe/Ui/ButtonDescriptor"; +import type { Popup } from "./Api/iframe/Ui/Popup"; + const wa = { ui, @@ -77,7 +78,7 @@ const wa = { /** * @deprecated Use WA.sound.loadSound instead */ - loadSound(url: string) : Sound { + loadSound(url: string): Sound { console.warn('Method WA.loadSound is deprecated. Please use WA.sound.loadSound instead'); return sound.loadSound(url); }, @@ -85,7 +86,7 @@ const wa = { /** * @deprecated Use WA.nav.goToPage instead */ - goToPage(url : string) : void { + goToPage(url: string): void { console.warn('Method WA.goToPage is deprecated. Please use WA.nav.goToPage instead'); nav.goToPage(url); }, @@ -101,7 +102,7 @@ const wa = { /** * @deprecated Use WA.nav.openCoWebSite instead */ - openCoWebSite(url : string) : void{ + openCoWebSite(url: string): void { console.warn('Method WA.openCoWebSite is deprecated. Please use WA.nav.openCoWebSite instead'); nav.openCoWebSite(url); }, From 8be29062f6c17bcd7eb8c5b225944b0b345dd4e4 Mon Sep 17 00:00:00 2001 From: jonny Date: Mon, 21 Jun 2021 18:41:41 +0200 Subject: [PATCH 3/3] reverted import sorting --- front/src/iframe_api.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/front/src/iframe_api.ts b/front/src/iframe_api.ts index 874f0ace..7b6b2db9 100644 --- a/front/src/iframe_api.ts +++ b/front/src/iframe_api.ts @@ -1,3 +1,4 @@ +import { registeredCallbacks } from "./Api/iframe/registeredCallbacks"; import { IframeResponseEvent, IframeResponseEventMap, @@ -5,17 +6,15 @@ import { TypedMessageEvent } from "./Api/Events/IframeEvent"; import chat from "./Api/iframe/chat"; -import controls from "./Api/iframe/controls"; import type { IframeCallback } from './Api/iframe/IframeApiContribution'; import nav from "./Api/iframe/nav"; -import { registeredCallbacks } from "./Api/iframe/registeredCallbacks"; -import room from "./Api/iframe/room"; -import sound from "./Api/iframe/sound"; -import type { Sound } from "./Api/iframe/Sound/Sound"; +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"; const wa = { ui,