Merge branch 'menu-commands-apiref' of github.com:jonnytest1/workadventure into metadataScriptingApi

This commit is contained in:
GRL 2021-06-23 09:18:20 +02:00
commit 85fe92f604
13 changed files with 233 additions and 192 deletions

View File

@ -65,3 +65,26 @@ WA.room.onLeaveZone('myZone', () => {
helloWorldPopup.close(); helloWorldPopup.close();
}); });
``` ```
### register additional menu entries
adds an additional Entry to the main menu , these exist until the map is unloaded
```typescript
WA.ui.registerMenuCommand(menuCommand: string, callback: (menuCommand: string) => void): void
```
Example:
```javascript
WA.ui.registerMenuCommand("test", () => {
WA.chat.sendChatMessage("test clicked", "menu cmd")
})
```
<div class="col">
<img src="./assets/menu-command.png" class="figure-img img-fluid rounded" alt="" />
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

View File

@ -15,7 +15,8 @@ import type { LayerEvent } from './LayerEvent';
import type { SetPropertyEvent } from "./setPropertyEvent"; import type { SetPropertyEvent } from "./setPropertyEvent";
import type { LoadSoundEvent } from "./LoadSoundEvent"; import type { LoadSoundEvent } from "./LoadSoundEvent";
import type { PlaySoundEvent } from "./PlaySoundEvent"; import type { PlaySoundEvent } from "./PlaySoundEvent";
import type { MenuItemClickedEvent } from "./MenuItemClickedEvent"; import type { MenuItemClickedEvent } from "./ui/MenuItemClickedEvent";
import type { MenuItemRegisterEvent } from './ui/MenuItemRegisterEvent';
import type { HasPlayerMovedEvent } from "./HasPlayerMovedEvent"; import type { HasPlayerMovedEvent } from "./HasPlayerMovedEvent";
@ -47,7 +48,7 @@ export type IframeEventMap = {
playSound: PlaySoundEvent playSound: PlaySoundEvent
stopSound: null, stopSound: null,
getState: undefined, getState: undefined,
registerMenuCommand: undefined registerMenuCommand: MenuItemRegisterEvent
} }
export interface IframeEvent<T extends keyof IframeEventMap> { export interface IframeEvent<T extends keyof IframeEventMap> {
type: T; type: T;

View File

@ -1,10 +0,0 @@
import * as tg from "generic-type-guard";
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<typeof isMenuItemRegisterEvent>;

View File

@ -8,3 +8,5 @@ export const isMenuItemClickedEvent =
* A message sent from the game to the iFrame when a menu item is clicked. * A message sent from the game to the iFrame when a menu item is clicked.
*/ */
export type MenuItemClickedEvent = tg.GuardedType<typeof isMenuItemClickedEvent>; export type MenuItemClickedEvent = tg.GuardedType<typeof isMenuItemClickedEvent>;

View File

@ -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<typeof isMenuItemRegisterEvent>;
export const isMenuItemRegisterIframeEvent =
new tg.IsInterface().withProperties({
type: tg.isSingletonString("registerMenuCommand"),
data: isMenuItemRegisterEvent
}).get();
const _registerMenuCommandStream: Subject<string> = new Subject();
export const registerMenuCommandStream = _registerMenuCommandStream.asObservable();
export function handleMenuItemRegistrationEvent(event: MenuItemRegisterEvent) {
_registerMenuCommandStream.next(event.menutItem)
}

View File

@ -24,12 +24,12 @@ import {isStopSoundEvent, StopSoundEvent} from "./Events/StopSoundEvent";
import {isLoadSoundEvent, LoadSoundEvent} from "./Events/LoadSoundEvent"; import {isLoadSoundEvent, LoadSoundEvent} from "./Events/LoadSoundEvent";
import {isSetPropertyEvent, SetPropertyEvent} from "./Events/setPropertyEvent"; import {isSetPropertyEvent, SetPropertyEvent} from "./Events/setPropertyEvent";
import {isLayerEvent, LayerEvent} from "./Events/LayerEvent"; import {isLayerEvent, LayerEvent} from "./Events/LayerEvent";
import {isMenuItemRegisterEvent} from "./Events/MenuItemRegisterEvent"; import {isMenuItemRegisterEvent,} from "./Events/ui/MenuItemRegisterEvent";
import type {DataLayerEvent} from "./Events/DataLayerEvent"; import type {DataLayerEvent} from "./Events/DataLayerEvent";
import type {GameStateEvent} from "./Events/GameStateEvent"; import type {GameStateEvent} from "./Events/GameStateEvent";
import type {MenuItemClickedEvent} from "./Events/MenuItemClickedEvent";
import type {HasPlayerMovedEvent} from "./Events/HasPlayerMovedEvent"; import type {HasPlayerMovedEvent} from "./Events/HasPlayerMovedEvent";
import {isLoadPageEvent} from "./Events/LoadPageEvent"; import {isLoadPageEvent} from "./Events/LoadPageEvent";
import {handleMenuItemRegistrationEvent, isMenuItemRegisterIframeEvent} from "./Events/ui/MenuItemRegisterEvent";
/** /**
* Listens to messages from iframes and turn those messages into easy to use observables. * Listens to messages from iframes and turn those messages into easy to use observables.
@ -115,21 +115,16 @@ class IframeListener {
// Note: maybe we could restrict on the domain too for additional security (in case the iframe goes to another domain). // Note: maybe we could restrict on the domain too for additional security (in case the iframe goes to another domain).
let foundSrc: string | undefined; let foundSrc: string | undefined;
foundSrc = [...this.scripts.keys()].find(key => { let iframe: HTMLIFrameElement;
return this.scripts.get(key)?.contentWindow == message.source
});
if (foundSrc === undefined) {
let iframe: HTMLIFrameElement;
for (iframe of this.iframes) { for (iframe of this.iframes) {
if (iframe.contentWindow === message.source) { if (iframe.contentWindow === message.source) {
foundSrc = iframe.src; foundSrc = iframe.src;
break;} break;
} }
}
if (foundSrc === undefined) { if (foundSrc === undefined) {
return; return;
}
} }
const payload = message.data; const payload = message.data;
@ -188,13 +183,13 @@ class IframeListener {
this.sendPlayerMove = true this.sendPlayerMove = true
} else if (payload.type == "getDataLayer") { } else if (payload.type == "getDataLayer") {
this._dataLayerChangeStream.next(); this._dataLayerChangeStream.next();
} else if (payload.type == "registerMenuCommand" && isMenuItemRegisterEvent(payload.data)) { } else if (isMenuItemRegisterIframeEvent(payload)) {
const data = payload.data.menutItem; const data = payload.data.menutItem;
// @ts-ignore // @ts-ignore
this.iframeCloseCallbacks.get(iframe).push(() => { this.iframeCloseCallbacks.get(iframe).push(() => {
this._unregisterMenuCommandStream.next(data); this._unregisterMenuCommandStream.next(data);
}) })
this._registerMenuCommandStream.next(payload.data.menutItem) handleMenuItemRegistrationEvent(payload.data)
} }
} }
}, false); }, false);
@ -295,15 +290,6 @@ class IframeListener {
this.scripts.delete(scriptUrl); this.scripts.delete(scriptUrl);
} }
sendMenuClickedEvent(menuItem: string) {
this.postMessage({
'type': 'menuItemClicked',
'data': {
menuItem: menuItem,
} as MenuItemClickedEvent
});
}
sendUserInputChat(message: string) { sendUserInputChat(message: string) {
this.postMessage({ this.postMessage({
'type': 'userInputChat', 'type': 'userInputChat',
@ -353,7 +339,7 @@ class IframeListener {
/** /**
* Sends the message... to all allowed iframes. * Sends the message... to all allowed iframes.
*/ */
private postMessage(message: IframeResponseEvent<keyof IframeResponseEventMap>) { public postMessage(message: IframeResponseEvent<keyof IframeResponseEventMap>) {
for (const iframe of this.iframes) { for (const iframe of this.iframes) {
iframe.contentWindow?.postMessage(message, '*'); iframe.contentWindow?.postMessage(message, '*');
} }

View File

@ -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
});
}

View File

@ -1,14 +1,17 @@
import { isButtonClickedEvent } from '../Events/ButtonClickedEvent'; 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 { IframeApiContribution, sendToWorkadventure } from './IframeApiContribution';
import { apiCallback } from "./registeredCallbacks"; 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; let popupId = 0;
const popups: Map<number, Popup> = new Map<number, Popup>(); const popups: Map<number, Popup> = new Map<number, Popup>();
const popupCallbacks: Map<number, Map<number, ButtonClickedCallback>> = new Map<number, Map<number, ButtonClickedCallback>>(); const popupCallbacks: Map<number, Map<number, ButtonClickedCallback>> = new Map<number, Map<number, ButtonClickedCallback>>();
const menuCallbacks: Map<string, (command: string) => void> = new Map()
interface ZonedPopupOptions { interface ZonedPopupOptions {
zone: string zone: string
objectLayerName?: string, objectLayerName?: string,
@ -33,6 +36,16 @@ class WorkAdventureUiCommands extends IframeApiContribution<WorkAdventureUiComma
callback(popup); callback(popup);
} }
} }
}),
apiCallback({
type: "menuItemClicked",
typeChecker: isMenuItemClickedEvent,
callback: event => {
const callback = menuCallbacks.get(event.menuItem);
if (callback) {
callback(event.menuItem)
}
}
})]; })];
@ -71,6 +84,16 @@ class WorkAdventureUiCommands extends IframeApiContribution<WorkAdventureUiComma
return popup; return popup;
} }
registerMenuCommand(commandDescriptor: string, callback: (commandDescriptor: string) => void) {
menuCallbacks.set(commandDescriptor, callback);
sendToWorkadventure({
'type': 'registerMenuCommand',
'data': {
menutItem: commandDescriptor
} as MenuItemRegisterEvent
});
}
displayBubble(): void { displayBubble(): void {
sendToWorkadventure({ 'type': 'displayBubble', data: null }); sendToWorkadventure({ 'type': 'displayBubble', data: null });
} }

View File

@ -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 { import type {
GroupCreatedUpdatedMessageInterface, GroupCreatedUpdatedMessageInterface,
MessageUserJoined, MessageUserJoined,
@ -9,94 +15,84 @@ import type {
PositionInterface, PositionInterface,
RoomJoinedMessageInterface RoomJoinedMessageInterface
} from "../../Connexion/ConnexionModels"; } 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 { import {
DEBUG_MODE, DEBUG_MODE,
JITSI_PRIVATE_MODE, JITSI_PRIVATE_MODE,
MAX_PER_GROUP, MAX_PER_GROUP,
POSITION_DELAY, POSITION_DELAY
} from "../../Enum/EnvironmentVariable"; } from "../../Enum/EnvironmentVariable";
import type { import { TextureError } from "../../Exception/TextureError";
ITiledMap, import type { UserMovedMessage } from "../../Messages/generated/messages_pb";
ITiledMapLayer, import { ProtobufClientUtils } from "../../Network/ProtobufClientUtils";
ITiledMapLayerProperty, import { peerStore } from "../../Stores/PeerStore";
ITiledMapObject, import { touchScreenManager } from "../../Touch/TouchScreenManager";
ITiledMapTileLayer, import { urlManager } from "../../Url/UrlManager";
ITiledTileSet import { audioManager } from "../../WebRtc/AudioManager";
} from "../Map/ITiledMap"; import { coWebsiteManager } from "../../WebRtc/CoWebsiteManager";
import type { AddPlayerInterface } from "./AddPlayerInterface"; import { HtmlUtils } from "../../WebRtc/HtmlUtils";
import { PlayerAnimationDirections } from "../Player/Animation"; import { jitsiFactory } from "../../WebRtc/JitsiFactory";
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 { import {
CenterListener, AUDIO_LOOP_PROPERTY, AUDIO_VOLUME_PROPERTY, CenterListener,
JITSI_MESSAGE_PROPERTIES, JITSI_MESSAGE_PROPERTIES,
layoutManager, layoutManager,
LayoutMode, LayoutMode,
ON_ACTION_TRIGGER_BUTTON, ON_ACTION_TRIGGER_BUTTON,
TRIGGER_JITSI_PROPERTIES, TRIGGER_JITSI_PROPERTIES,
TRIGGER_WEBSITE_PROPERTIES, TRIGGER_WEBSITE_PROPERTIES,
WEBSITE_MESSAGE_PROPERTIES, WEBSITE_MESSAGE_PROPERTIES
AUDIO_VOLUME_PROPERTY,
AUDIO_LOOP_PROPERTY
} from "../../WebRtc/LayoutManager"; } from "../../WebRtc/LayoutManager";
import { GameMap } from "./GameMap";
import { coWebsiteManager } from "../../WebRtc/CoWebsiteManager";
import { mediaManager } from "../../WebRtc/MediaManager"; import { mediaManager } from "../../WebRtc/MediaManager";
import type { ItemFactoryInterface } from "../Items/ItemFactoryInterface"; import { SimplePeer, UserSimplePeerInterface } from "../../WebRtc/SimplePeer";
import type { ActionableItem } from "../Items/ActionableItem"; import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager";
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 { 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 { 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,
ITiledMapLayerProperty,
ITiledMapObject,
ITiledMapTileLayer,
ITiledTileSet } from "../Map/ITiledMap";
import { MenuScene, MenuSceneName } from '../Menu/MenuScene';
import { PlayerAnimationDirections } from "../Player/Animation";
import { hasMovedEventName, Player, requestEmoteEventName } from "../Player/Player";
import { ErrorSceneName } from "../Reconnecting/ErrorScene"; import { ErrorSceneName } from "../Reconnecting/ErrorScene";
import { localUserStore } from "../../Connexion/LocalUserStore"; import { ReconnectingSceneName } from "../Reconnecting/ReconnectingScene";
import { iframeListener } from "../../Api/IframeListener"; import { waScaleManager } from "../Services/WaScaleManager";
import { HtmlUtils } from "../../WebRtc/HtmlUtils"; 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 } from "./GameManager";
import { GameMap } from "./GameMap";
import { PlayerMovement } from "./PlayerMovement";
import { PlayersPositionInterpolator } from "./PlayersPositionInterpolator";
import { soundManager } from "./SoundManager";
import Texture = Phaser.Textures.Texture; import Texture = Phaser.Textures.Texture;
import Sprite = Phaser.GameObjects.Sprite; import Sprite = Phaser.GameObjects.Sprite;
import CanvasTexture = Phaser.Textures.CanvasTexture; import CanvasTexture = Phaser.Textures.CanvasTexture;
import GameObject = Phaser.GameObjects.GameObject; import GameObject = Phaser.GameObjects.GameObject;
import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR; import FILE_LOAD_ERROR = Phaser.Loader.Events.FILE_LOAD_ERROR;
import DOMElement = Phaser.GameObjects.DOMElement; import DOMElement = Phaser.GameObjects.DOMElement;
import EVENT_TYPE =Phaser.Scenes.Events import EVENT_TYPE = Phaser.Scenes.Events
import type {Subscription} from "rxjs";
import {worldFullMessageStream} from "../../Connexion/WorldFullMessageStream";
import { lazyLoadCompanionResource } from "../Companion/CompanionTexturesLoadingManager";
import RenderTexture = Phaser.GameObjects.RenderTexture; import RenderTexture = Phaser.GameObjects.RenderTexture;
import Tilemap = Phaser.Tilemaps.Tilemap; 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";
import type { HasPlayerMovedEvent } from '../../Api/Events/HasPlayerMovedEvent'; import type { HasPlayerMovedEvent } from '../../Api/Events/HasPlayerMovedEvent';
import { MenuScene, MenuSceneName } from '../Menu/MenuScene';
import AnimatedTiles from "phaser-animated-tiles"; import AnimatedTiles from "phaser-animated-tiles";
@ -192,7 +188,7 @@ export class GameScene extends DirtyScene implements CenterListener {
private characterLayers!: string[]; private characterLayers!: string[];
private companion!: string | null; private companion!: string | null;
private messageSubscription: Subscription | null = null; private messageSubscription: Subscription | null = null;
private popUpElements : Map<number, DOMElement> = new Map<number, Phaser.GameObjects.DOMElement>(); private popUpElements: Map<number, DOMElement> = new Map<number, Phaser.GameObjects.DOMElement>();
private originalMapUrl: string | undefined; private originalMapUrl: string | undefined;
private pinchManager: PinchManager | undefined; private pinchManager: PinchManager | undefined;
private mapTransitioning: boolean = false; //used to prevent transitions happenning at the same time. private mapTransitioning: boolean = false; //used to prevent transitions happenning at the same time.
@ -899,37 +895,34 @@ ${escapedMessage}
this.userInputManager.disableControls(); this.userInputManager.disableControls();
})); }));
this.iframeSubscriptionList.push(iframeListener.playSoundStream.subscribe((playSoundEvent)=> this.iframeSubscriptionList.push(iframeListener.playSoundStream.subscribe((playSoundEvent) => {
{ const url = new URL(playSoundEvent.url, this.MapUrlFile);
const url = new URL(playSoundEvent.url, this.MapUrlFile); soundManager.playSound(this.load, this.sound, url.toString(), playSoundEvent.config);
soundManager.playSound(this.load,this.sound,url.toString(),playSoundEvent.config);
}))
this.iframeSubscriptionList.push(iframeListener.stopSoundStream.subscribe((stopSoundEvent)=>
{
const url = new URL(stopSoundEvent.url, this.MapUrlFile);
soundManager.stopSound(this.sound,url.toString());
})) }))
this.iframeSubscriptionList.push(iframeListener.loadSoundStream.subscribe((loadSoundEvent)=> this.iframeSubscriptionList.push(iframeListener.stopSoundStream.subscribe((stopSoundEvent) => {
{ const url = new URL(stopSoundEvent.url, this.MapUrlFile);
soundManager.stopSound(this.sound, url.toString());
}))
this.iframeSubscriptionList.push(iframeListener.loadSoundStream.subscribe((loadSoundEvent) => {
const url = new URL(loadSoundEvent.url, this.MapUrlFile); 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.userInputManager.restoreControls();
})); }));
this.iframeSubscriptionList.push(iframeListener.loadPageStream.subscribe((url:string)=>{ this.iframeSubscriptionList.push(iframeListener.loadPageStream.subscribe((url: string) => {
this.loadNextGame(url).then(()=>{ this.loadNextGame(url).then(() => {
this.events.once(EVENT_TYPE.POST_UPDATE,()=>{ this.events.once(EVENT_TYPE.POST_UPDATE, () => {
this.onMapExit(url); this.onMapExit(url);
}) })
}) })
})); }));
let scriptedBubbleSprite : Sprite; let scriptedBubbleSprite: Sprite;
this.iframeSubscriptionList.push(iframeListener.displayBubbleStream.subscribe(()=>{ this.iframeSubscriptionList.push(iframeListener.displayBubbleStream.subscribe(() => {
scriptedBubbleSprite = new Sprite(this,this.CurrentPlayer.x + 25,this.CurrentPlayer.y,'circleSprite-white'); scriptedBubbleSprite = new Sprite(this, this.CurrentPlayer.x + 25, this.CurrentPlayer.y, 'circleSprite-white');
scriptedBubbleSprite.setDisplayOrigin(48, 48); scriptedBubbleSprite.setDisplayOrigin(48, 48);
this.add.existing(scriptedBubbleSprite); this.add.existing(scriptedBubbleSprite);
})); }));
@ -1000,8 +993,8 @@ ${escapedMessage}
private onMapExit(exitKey: string) { private onMapExit(exitKey: string) {
if (this.mapTransitioning) return; if (this.mapTransitioning) return;
this.mapTransitioning = true; this.mapTransitioning = true;
const {roomId, hash} = Room.getIdFromIdentifier(exitKey, this.MapUrlFile, this.instance); 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); if (!roomId) throw new Error('Could not find the room from its exit key: ' + exitKey);
urlManager.pushStartLayerNameToUrl(hash); urlManager.pushStartLayerNameToUrl(hash);
const menuScene: MenuScene = this.scene.get(MenuSceneName) as MenuScene const menuScene: MenuScene = this.scene.get(MenuSceneName) as MenuScene
menuScene.reset() menuScene.reset()
@ -1159,7 +1152,7 @@ ${escapedMessage}
private loadNextGame(exitSceneIdentifier: string) : Promise<void>{ private loadNextGame(exitSceneIdentifier: string) : Promise<void>{
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); const room = new Room(roomId);
return gameManager.loadMap(room, this.scene).catch(() => {}); return gameManager.loadMap(room, this.scene).catch(() => { });
} }
private startUser(layer: ITiledMapTileLayer): PositionInterface { private startUser(layer: ITiledMapTileLayer): PositionInterface {

View File

@ -14,6 +14,8 @@ import { HtmlUtils } from '../../WebRtc/HtmlUtils';
import { iframeListener } from '../../Api/IframeListener'; import { iframeListener } from '../../Api/IframeListener';
import { Subscription } from 'rxjs'; import { Subscription } from 'rxjs';
import { videoConstraintStore } from "../../Stores/MediaStore"; import { videoConstraintStore } from "../../Stores/MediaStore";
import {registerMenuCommandStream} from "../../Api/Events/ui/MenuItemRegisterEvent";
import {sendMenuClickedEvent} from "../../Api/iframe/Ui/MenuItem";
export const MenuSceneName = 'MenuScene'; export const MenuSceneName = 'MenuScene';
const gameMenuKey = 'gameMenu'; const gameMenuKey = 'gameMenu';
@ -42,12 +44,12 @@ export class MenuScene extends Phaser.Scene {
private warningContainerTimeout: NodeJS.Timeout | null = null; private warningContainerTimeout: NodeJS.Timeout | null = null;
private subscriptions = new Subscription() private subscriptions = new Subscription()
constructor() { constructor() {
super({key: MenuSceneName}); super({ key: MenuSceneName });
this.gameQualityValue = localUserStore.getGameQualityValue(); this.gameQualityValue = localUserStore.getGameQualityValue();
this.videoQualityValue = localUserStore.getVideoQualityValue(); this.videoQualityValue = localUserStore.getVideoQualityValue();
this.subscriptions.add(iframeListener.registerMenuCommandStream.subscribe(menuCommand => { this.subscriptions.add(registerMenuCommandStream.subscribe(menuCommand => {
this.addMenuOption(menuCommand); this.addMenuOption(menuCommand);
})) }))
@ -56,7 +58,25 @@ export class MenuScene extends Phaser.Scene {
})) }))
} }
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 = `<button class="fromApi" id="${escapedHtml}">${escapedHtml}</button>`
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(gameMenuKey, 'resources/html/gameMenu.html');
this.load.html(gameMenuIconKey, 'resources/html/gameMenuIcon.html'); this.load.html(gameMenuIconKey, 'resources/html/gameMenuIcon.html');
this.load.html(gameSettingsMenuKey, 'resources/html/gameQualityMenu.html'); this.load.html(gameSettingsMenuKey, 'resources/html/gameQualityMenu.html');
@ -65,13 +85,6 @@ export class MenuScene extends Phaser.Scene {
this.load.html(warningContainerKey, warningContainerHtml); this.load.html(warningContainerKey, warningContainerHtml);
} }
reset() {
const addedMenuItems=[...this.menuElement.node.querySelectorAll(".fromApi")];
for(let index=addedMenuItems.length-1;index>=0;index--){
addedMenuItems[index].remove()
}
}
create() { create() {
menuIconVisible.set(true); menuIconVisible.set(true);
this.menuElement = this.add.dom(closedSideMenuX, 30).createFromCache(gameMenuKey); this.menuElement = this.add.dom(closedSideMenuX, 30).createFromCache(gameMenuKey);
@ -86,11 +99,11 @@ export class MenuScene extends Phaser.Scene {
this.gameShareElement = this.add.dom(middleX, -400).createFromCache(gameShare); this.gameShareElement = this.add.dom(middleX, -400).createFromCache(gameShare);
MenuScene.revealMenusAfterInit(this.gameShareElement, gameShare); MenuScene.revealMenusAfterInit(this.gameShareElement, gameShare);
this.gameShareElement.addListener('click'); this.gameShareElement.addListener('click');
this.gameShareElement.on('click', (event:MouseEvent) => { this.gameShareElement.on('click', (event: MouseEvent) => {
event.preventDefault(); event.preventDefault();
if((event?.target as HTMLInputElement).id === 'gameShareFormSubmit') { if ((event?.target as HTMLInputElement).id === 'gameShareFormSubmit') {
this.copyLink(); this.copyLink();
}else if((event?.target as HTMLInputElement).id === 'gameShareFormCancel') { } else if ((event?.target as HTMLInputElement).id === 'gameShareFormCancel') {
this.closeGameShare(); this.closeGameShare();
} }
}); });
@ -146,8 +159,8 @@ export class MenuScene extends Phaser.Scene {
} }
//TODO bind with future metadata of card //TODO bind with future metadata of card
//if (connectionManager.getConnexionType === GameConnexionTypes.anonymous){ //if (connectionManager.getConnexionType === GameConnexionTypes.anonymous){
const adminSection = this.menuElement.getChildByID('socialLinks') as HTMLElement; const adminSection = this.menuElement.getChildByID('socialLinks') as HTMLElement;
adminSection.hidden = false; adminSection.hidden = false;
//} //}
this.tweens.add({ this.tweens.add({
targets: this.menuElement, targets: this.menuElement,
@ -197,28 +210,28 @@ export class MenuScene extends Phaser.Scene {
this.settingsMenuOpened = true; this.settingsMenuOpened = true;
const gameQualitySelect = this.gameQualityMenuElement.getChildByID('select-game-quality') as HTMLInputElement; 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; const videoQualitySelect = this.gameQualityMenuElement.getChildByID('select-video-quality') as HTMLInputElement;
videoQualitySelect.value = ''+this.videoQualityValue; videoQualitySelect.value = '' + this.videoQualityValue;
this.gameQualityMenuElement.addListener('click'); this.gameQualityMenuElement.addListener('click');
this.gameQualityMenuElement.on('click', (event:MouseEvent) => { this.gameQualityMenuElement.on('click', (event: MouseEvent) => {
event.preventDefault(); event.preventDefault();
if ((event?.target as HTMLInputElement).id === 'gameQualityFormSubmit') { if ((event?.target as HTMLInputElement).id === 'gameQualityFormSubmit') {
const gameQualitySelect = this.gameQualityMenuElement.getChildByID('select-game-quality') as HTMLInputElement; const gameQualitySelect = this.gameQualityMenuElement.getChildByID('select-game-quality') as HTMLInputElement;
const videoQualitySelect = this.gameQualityMenuElement.getChildByID('select-video-quality') as HTMLInputElement; const videoQualitySelect = this.gameQualityMenuElement.getChildByID('select-video-quality') as HTMLInputElement;
this.saveSetting(parseInt(gameQualitySelect.value), parseInt(videoQualitySelect.value)); 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(); this.closeGameQualityMenu();
} }
}); });
let middleY = this.scale.height / 2 - 392/2; let middleY = this.scale.height / 2 - 392 / 2;
if(middleY < 0){ if (middleY < 0) {
middleY = 0; middleY = 0;
} }
let middleX = this.scale.width / 2 - 457/2; let middleX = this.scale.width / 2 - 457 / 2;
if(middleX < 0){ if (middleX < 0) {
middleX = 0; middleX = 0;
} }
this.tweens.add({ this.tweens.add({
@ -244,7 +257,7 @@ export class MenuScene extends Phaser.Scene {
} }
private openGameShare(): void{ private openGameShare(): void {
if (this.gameShareOpened) { if (this.gameShareOpened) {
this.closeGameShare(); this.closeGameShare();
return; return;
@ -258,11 +271,11 @@ export class MenuScene extends Phaser.Scene {
this.gameShareOpened = true; this.gameShareOpened = true;
let middleY = this.scale.height / 2 - 85; let middleY = this.scale.height / 2 - 85;
if(middleY < 0){ if (middleY < 0) {
middleY = 0; middleY = 0;
} }
let middleX = this.scale.width / 2 - 200; let middleX = this.scale.width / 2 - 200;
if(middleX < 0){ if (middleX < 0) {
middleX = 0; middleX = 0;
} }
this.tweens.add({ this.tweens.add({
@ -274,7 +287,7 @@ export class MenuScene extends Phaser.Scene {
}); });
} }
private closeGameShare(): void{ private closeGameShare(): void {
const gameShareInfo = this.gameShareElement.getChildByID('gameShareInfo') as HTMLParagraphElement; const gameShareInfo = this.gameShareElement.getChildByID('gameShareInfo') as HTMLParagraphElement;
gameShareInfo.innerText = ''; gameShareInfo.innerText = '';
gameShareInfo.style.display = 'none'; gameShareInfo.style.display = 'none';
@ -287,17 +300,6 @@ export class MenuScene extends Phaser.Scene {
}); });
} }
public addMenuOption(menuText: string) {
const wrappingSection = document.createElement("section")
const excapedHtml = HtmlUtils.escapeHtml(menuText);
wrappingSection.innerHTML = `<button class="fromApi" id="${excapedHtml}">${excapedHtml}</button>`
const menuItemContainer = this.menuElement.node.querySelector("#gameMenu main");
if (menuItemContainer) {
menuItemContainer.querySelector(`#${excapedHtml}.fromApi`)?.remove()
menuItemContainer.insertBefore(wrappingSection, menuItemContainer.querySelector("#socialLinks"))
}
}
private onMenuClick(event: MouseEvent) { private onMenuClick(event: MouseEvent) {
const htmlMenuItem = (event?.target as HTMLInputElement); const htmlMenuItem = (event?.target as HTMLInputElement);
if (htmlMenuItem.classList.contains('not-button')) { if (htmlMenuItem.classList.contains('not-button')) {
@ -306,11 +308,11 @@ export class MenuScene extends Phaser.Scene {
event.preventDefault(); event.preventDefault();
if (htmlMenuItem.classList.contains("fromApi")) { if (htmlMenuItem.classList.contains("fromApi")) {
iframeListener.sendMenuClickedEvent(htmlMenuItem.id) sendMenuClickedEvent(htmlMenuItem.id)
return return
} }
switch (htmlMenuItem.id) { switch ((event?.target as HTMLInputElement).id) {
case 'changeNameButton': case 'changeNameButton':
this.closeSideMenu(); this.closeSideMenu();
gameManager.leaveGame(this, LoginSceneName, new LoginScene()); gameManager.leaveGame(this, LoginSceneName, new LoginScene());
@ -351,7 +353,7 @@ export class MenuScene extends Phaser.Scene {
gameShareInfo.style.display = 'block'; gameShareInfo.style.display = 'block';
} }
private saveSetting(valueGame: number, valueVideo: number){ private saveSetting(valueGame: number, valueVideo: number) {
if (valueGame !== this.gameQualityValue) { if (valueGame !== this.gameQualityValue) {
this.gameQualityValue = valueGame; this.gameQualityValue = valueGame;
localUserStore.setGameQualityValue(valueGame); localUserStore.setGameQualityValue(valueGame);
@ -372,7 +374,7 @@ export class MenuScene extends Phaser.Scene {
window.open(sparkHost, '_blank'); window.open(sparkHost, '_blank');
} }
private closeAll(){ private closeAll() {
this.closeGameQualityMenu(); this.closeGameQualityMenu();
this.closeGameShare(); this.closeGameShare();
this.gameReportElement.close(); this.gameReportElement.close();

View File

@ -1,4 +1,4 @@
import {registeredCallbacks} from "./Api/iframe/registeredCallbacks"; import { registeredCallbacks } from "./Api/iframe/registeredCallbacks";
import { import {
IframeResponseEvent, IframeResponseEvent,
IframeResponseEventMap, IframeResponseEventMap,
@ -6,19 +6,16 @@ import {
TypedMessageEvent TypedMessageEvent
} from "./Api/Events/IframeEvent"; } from "./Api/Events/IframeEvent";
import chat from "./Api/iframe/chat"; import chat from "./Api/iframe/chat";
import type {IframeCallback} from './Api/iframe/IframeApiContribution'; import type { IframeCallback } from './Api/iframe/IframeApiContribution';
import nav from "./Api/iframe/nav"; import nav from "./Api/iframe/nav";
import controls from "./Api/iframe/controls"; import controls from "./Api/iframe/controls";
import ui from "./Api/iframe/ui"; import ui from "./Api/iframe/ui";
import sound from "./Api/iframe/sound"; import sound from "./Api/iframe/sound";
import room from "./Api/iframe/room"; import room from "./Api/iframe/room";
import type {ButtonDescriptor} from "./Api/iframe/Ui/ButtonDescriptor"; import type { ButtonDescriptor } from "./Api/iframe/Ui/ButtonDescriptor";
import type {Popup} from "./Api/iframe/Ui/Popup"; import type { Popup } from "./Api/iframe/Ui/Popup";
import type {Sound} from "./Api/iframe/Sound/Sound"; import type { Sound } from "./Api/iframe/Sound/Sound";
import type {MenuItemRegisterEvent} from "./Api/Events/MenuItemRegisterEvent";
const menuCallbacks: Map<string, (command: string) => void> = new Map()
const wa = { const wa = {
ui, ui,
nav, nav,
@ -80,7 +77,7 @@ const wa = {
/** /**
* @deprecated Use WA.sound.loadSound instead * @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'); console.warn('Method WA.loadSound is deprecated. Please use WA.sound.loadSound instead');
return sound.loadSound(url); return sound.loadSound(url);
}, },
@ -88,7 +85,7 @@ const wa = {
/** /**
* @deprecated Use WA.nav.goToPage instead * @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'); console.warn('Method WA.goToPage is deprecated. Please use WA.nav.goToPage instead');
nav.goToPage(url); nav.goToPage(url);
}, },
@ -124,16 +121,6 @@ const wa = {
console.warn('Method WA.openPopup is deprecated. Please use WA.ui.openPopup instead'); console.warn('Method WA.openPopup is deprecated. Please use WA.ui.openPopup instead');
return ui.openPopup(targetObject, message, buttons); return ui.openPopup(targetObject, message, buttons);
}, },
registerMenuCommand(commandDescriptor: string, callback: (commandDescriptor: string) => void): void {
menuCallbacks.set(commandDescriptor, callback);
window.parent.postMessage({
'type': 'registerMenuCommand',
'data': {
menutItem: commandDescriptor
} as MenuItemRegisterEvent
}, '*');
},
/** /**
* @deprecated Use WA.chat.onChatMessage instead * @deprecated Use WA.chat.onChatMessage instead
*/ */

View File

@ -5,11 +5,9 @@
</head> </head>
<body> <body>
<script> <script>
let chatbotEnabled = false; WA.ui.registerMenuCommand("test", () => {
WA.registerMenuCommand('help', () => { WA.chat.sendChatMessage("test clicked", "menu cmd")
chatbotEnabled = true; })
WA.sendChatMessage("HELP", 'Mr Robot');
});
</script> </script>
</body> </body>
</html> </html>