Merge branch 'develop' of github.com:thecodingmachine/workadventure into layoutManagerSvelte

This commit is contained in:
GRL 2021-08-03 11:40:51 +02:00
commit 2d8098b06d
42 changed files with 987 additions and 8874 deletions

View file

@ -60,6 +60,7 @@
<section>
<button id="enableNotification">Enable notifications</button>
</section>
<!-- TODO activate authentication -->
<section hidden>
<button id="oidcLogin">Oauth Login</button>
</section>

8393
front/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -51,6 +51,7 @@
"phaser3-rex-plugins": "^1.1.42",
"queue-typescript": "^1.0.1",
"quill": "1.3.6",
"quill-delta-to-html": "^0.12.0",
"rxjs": "^6.6.3",
"simple-peer": "^9.11.0",
"socket.io-client": "^2.3.0",

View file

@ -1,92 +0,0 @@
import {HtmlUtils} from "./../WebRtc/HtmlUtils";
import {PUSHER_URL, UPLOADER_URL} from "../Enum/EnvironmentVariable";
import type {RoomConnection} from "../Connexion/RoomConnection";
import type {PlayGlobalMessageInterface} from "../Connexion/ConnexionModels";
import {soundPlayingStore} from "../Stores/SoundPlayingStore";
import {soundManager} from "../Phaser/Game/SoundManager";
import {AdminMessageEventTypes} from "../Connexion/AdminMessagesService";
export class GlobalMessageManager {
constructor(private Connection: RoomConnection) {
this.initialise();
}
initialise(){
//receive signal to show message
this.Connection.receivePlayGlobalMessage((message: PlayGlobalMessageInterface) => {
this.playMessage(message);
});
//receive signal to close message
this.Connection.receiveStopGlobalMessage((messageId: string) => {
this.stopMessage(messageId);
});
//receive signal to close message
this.Connection.receiveTeleportMessage((map: string) => {
console.log('map to teleport user', map);
//TODO teleport user on map
});
}
private playMessage(message : PlayGlobalMessageInterface){
const previousMessage = document.getElementById(this.getHtmlMessageId(message.id));
if(previousMessage){
previousMessage.remove();
}
if(AdminMessageEventTypes.audio === message.type){
this.playAudioMessage(message.id, message.message);
}
if(AdminMessageEventTypes.admin === message.type){
this.playTextMessage(message.id, message.message);
}
}
private playAudioMessage(messageId : string, urlMessage: string) {
soundPlayingStore.playSound(UPLOADER_URL + urlMessage);
}
private playTextMessage(messageId : string, htmlMessage: string){
//add button to clear message
const buttonText = document.createElement('p');
buttonText.id = 'button-clear-message';
buttonText.innerText = 'Clear';
const buttonMainConsole = document.createElement('div');
buttonMainConsole.classList.add('clear');
buttonMainConsole.appendChild(buttonText);
buttonMainConsole.addEventListener('click', () => {
messageContainer.style.top = '-80%';
setTimeout(() => {
messageContainer.remove();
buttonMainConsole.remove();
});
});
//create content message
const messageCotent = document.createElement('div');
messageCotent.innerHTML = htmlMessage;
messageCotent.className = "content-message";
//add message container
const messageContainer = document.createElement('div');
messageContainer.id = this.getHtmlMessageId(messageId);
messageContainer.className = "message-container";
messageContainer.appendChild(messageCotent);
messageContainer.appendChild(buttonMainConsole);
const mainSectionDiv = HtmlUtils.getElementByIdOrFail<HTMLDivElement>('main-container');
mainSectionDiv.appendChild(messageContainer);
}
private stopMessage(messageId: string){
HtmlUtils.removeElementByIdOrFail<HTMLDivElement>(this.getHtmlMessageId(messageId));
}
private getHtmlMessageId(messageId: string) : string{
return `message-${messageId}`;
}
}

View file

@ -1,95 +0,0 @@
import type {TypeMessageInterface} from "./UserMessageManager";
import {HtmlUtils} from "../WebRtc/HtmlUtils";
let modalTimeOut : NodeJS.Timeout;
export class TypeMessageExt implements TypeMessageInterface{
private nbSecond = 0;
private maxNbSecond = 10;
private titleMessage = 'IMPORTANT !';
showMessage(message: string, canDeleteMessage: boolean = true): void {
//delete previous modal
try{
if(modalTimeOut){
clearTimeout(modalTimeOut);
}
const modal = HtmlUtils.getElementByIdOrFail('report-message-user');
modal.remove();
}catch (err){
console.error(err);
}
//create new modal
const div : HTMLDivElement = document.createElement('div');
div.classList.add('modal-report-user');
div.id = 'report-message-user';
div.style.backgroundColor = '#000000e0';
const img : HTMLImageElement = document.createElement('img');
img.src = 'resources/logos/report.svg';
div.appendChild(img);
const title : HTMLParagraphElement = document.createElement('p');
title.id = 'title-report-user';
title.innerText = `${this.titleMessage} (${this.maxNbSecond})`;
div.appendChild(title);
const p : HTMLParagraphElement = document.createElement('p');
p.id = 'body-report-user'
p.innerText = message;
div.appendChild(p);
const mainSectionDiv = HtmlUtils.getElementByIdOrFail<HTMLDivElement>('main-container');
mainSectionDiv.appendChild(div);
const reportMessageAudio = HtmlUtils.getElementByIdOrFail<HTMLAudioElement>('report-message');
// FIXME: this will fail on iOS
// We should move the sound playing into the GameScene and listen to the event of a report using a store
try {
reportMessageAudio.play();
} catch (e) {
console.error(e);
}
this.nbSecond = this.maxNbSecond;
setTimeout((c) => {
this.forMessage(title, canDeleteMessage);
}, 1000);
}
forMessage(title: HTMLParagraphElement, canDeleteMessage: boolean = true){
this.nbSecond -= 1;
title.innerText = `${this.titleMessage} (${this.nbSecond})`;
if(this.nbSecond > 0){
modalTimeOut = setTimeout(() => {
this.forMessage(title, canDeleteMessage);
}, 1000);
}else {
title.innerText = this.titleMessage;
if (!canDeleteMessage) {
return;
}
const imgCancel: HTMLImageElement = document.createElement('img');
imgCancel.id = 'cancel-report-user';
imgCancel.src = 'resources/logos/close.svg';
const div = HtmlUtils.getElementByIdOrFail<HTMLDivElement>('report-message-user');
div.appendChild(imgCancel);
imgCancel.addEventListener('click', () => {
div.remove();
});
}
}
}
export class Message extends TypeMessageExt {}
export class Ban extends TypeMessageExt {}
export class Banned extends TypeMessageExt {
showMessage(message: string){
super.showMessage(message, false);
}
}

View file

@ -1,43 +1,34 @@
import * as TypeMessages from "./TypeMessage";
import {Banned} from "./TypeMessage";
import {adminMessagesService} from "../Connexion/AdminMessagesService";
export interface TypeMessageInterface {
showMessage(message: string): void;
}
import { AdminMessageEventTypes, adminMessagesService } from "../Connexion/AdminMessagesService";
import { textMessageContentStore, textMessageVisibleStore } from "../Stores/TypeMessageStore/TextMessageStore";
import { soundPlayingStore } from "../Stores/SoundPlayingStore";
import { UPLOADER_URL } from "../Enum/EnvironmentVariable";
import { banMessageContentStore, banMessageVisibleStore } from "../Stores/TypeMessageStore/BanMessageStore";
class UserMessageManager {
typeMessages: Map<string, TypeMessageInterface> = new Map<string, TypeMessageInterface>();
receiveBannedMessageListener!: Function;
constructor() {
const valueTypeMessageTab = Object.values(TypeMessages);
Object.keys(TypeMessages).forEach((value: string, index: number) => {
const typeMessageInstance: TypeMessageInterface = (new valueTypeMessageTab[index]() as TypeMessageInterface);
this.typeMessages.set(value.toLowerCase(), typeMessageInstance);
});
adminMessagesService.messageStream.subscribe((event) => {
const typeMessage = this.showMessage(event.type, event.text);
if(typeMessage instanceof Banned) {
textMessageVisibleStore.set(false);
banMessageVisibleStore.set(false);
if (event.type === AdminMessageEventTypes.admin) {
textMessageContentStore.set(event.text);
textMessageVisibleStore.set(true);
} else if (event.type === AdminMessageEventTypes.audio) {
soundPlayingStore.playSound(UPLOADER_URL + event.text);
} else if (event.type === AdminMessageEventTypes.ban) {
banMessageContentStore.set(event.text);
banMessageVisibleStore.set(true);
} else if (event.type === AdminMessageEventTypes.banned) {
banMessageContentStore.set(event.text);
banMessageVisibleStore.set(true);
this.receiveBannedMessageListener();
}
})
});
}
showMessage(type: string, message: string) {
const classTypeMessage = this.typeMessages.get(type.toLowerCase());
if (!classTypeMessage) {
console.error('Message unknown');
return;
}
classTypeMessage.showMessage(message);
return classTypeMessage;
}
setReceiveBanListener(callback: Function){
setReceiveBanListener(callback: Function) {
this.receiveBannedMessageListener = callback;
}
}
export const userMessageManager = new UserMessageManager()
export const userMessageManager = new UserMessageManager();

View file

@ -1,5 +1,4 @@
import * as tg from "generic-type-guard";
import type { GameStateEvent } from "./GameStateEvent";
import type { ButtonClickedEvent } from "./ButtonClickedEvent";
import type { ChatEvent } from "./ChatEvent";
import type { ClosePopupEvent } from "./ClosePopupEvent";
@ -10,7 +9,6 @@ import type { OpenCoWebSiteEvent } from "./OpenCoWebSiteEvent";
import type { OpenPopupEvent } from "./OpenPopupEvent";
import type { OpenTabEvent } from "./OpenTabEvent";
import type { UserInputChatEvent } from "./UserInputChatEvent";
import type { MapDataEvent } from "./MapDataEvent";
import type { LayerEvent } from "./LayerEvent";
import type { SetPropertyEvent } from "./setPropertyEvent";
import type { LoadSoundEvent } from "./LoadSoundEvent";
@ -20,9 +18,11 @@ import type { MenuItemRegisterEvent } from "./ui/MenuItemRegisterEvent";
import type { HasPlayerMovedEvent } from "./HasPlayerMovedEvent";
import type { SetTilesEvent } from "./SetTilesEvent";
import type { SetVariableEvent } from "./SetVariableEvent";
import {isGameStateEvent} from "./GameStateEvent";
import {isMapDataEvent} from "./MapDataEvent";
import {isSetVariableEvent} from "./SetVariableEvent";
import { isGameStateEvent } from "./GameStateEvent";
import { isMapDataEvent } from "./MapDataEvent";
import { isSetVariableEvent } from "./SetVariableEvent";
import type { LoadTilesetEvent } from "./LoadTilesetEvent";
import { isLoadTilesetEvent } from "./LoadTilesetEvent";
export interface TypedMessageEvent<T> extends MessageEvent {
data: T;
@ -52,6 +52,7 @@ export type IframeEventMap = {
playSound: PlaySoundEvent;
stopSound: null;
getState: undefined;
loadTileset: LoadTilesetEvent;
registerMenuCommand: MenuItemRegisterEvent;
setTiles: SetTilesEvent;
};
@ -83,7 +84,6 @@ export const isIframeResponseEventWrapper = (event: {
type?: string;
}): event is IframeResponseEvent<keyof IframeResponseEventMap> => typeof event.type === "string";
/**
* List event types sent from an iFrame to WorkAdventure that expect a unique answer from WorkAdventure along the type for the answer from WorkAdventure to the iFrame.
* Types are defined using Type guards that will actually bused to enforce and check types.
@ -101,22 +101,26 @@ export const iframeQueryMapTypeGuards = {
query: isSetVariableEvent,
answer: tg.isUndefined,
},
}
loadTileset: {
query: isLoadTilesetEvent,
answer: tg.isNumber,
},
};
type GuardedType<T> = T extends (x: unknown) => x is (infer T) ? T : never;
type GuardedType<T> = T extends (x: unknown) => x is infer T ? T : never;
type IframeQueryMapTypeGuardsType = typeof iframeQueryMapTypeGuards;
type UnknownToVoid<T> = undefined extends T ? void : T;
type UnknownToVoid<T> = undefined extends T ? void : T;
export type IframeQueryMap = {
[key in keyof IframeQueryMapTypeGuardsType]: {
query: GuardedType<IframeQueryMapTypeGuardsType[key]['query']>
answer: UnknownToVoid<GuardedType<IframeQueryMapTypeGuardsType[key]['answer']>>
}
}
query: GuardedType<IframeQueryMapTypeGuardsType[key]["query"]>;
answer: UnknownToVoid<GuardedType<IframeQueryMapTypeGuardsType[key]["answer"]>>;
};
};
export interface IframeQuery<T extends keyof IframeQueryMap> {
type: T;
data: IframeQueryMap[T]['query'];
data: IframeQueryMap[T]["query"];
}
export interface IframeQueryWrapper<T extends keyof IframeQueryMap> {
@ -126,30 +130,34 @@ export interface IframeQueryWrapper<T extends keyof IframeQueryMap> {
export const isIframeQueryKey = (type: string): type is keyof IframeQueryMap => {
return type in iframeQueryMapTypeGuards;
}
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const isIframeQuery = (event: any): event is IframeQuery<keyof IframeQueryMap> => {
const type = event.type;
if (typeof type !== 'string') {
if (typeof type !== "string") {
return false;
}
if (!isIframeQueryKey(type)) {
return false;
}
return iframeQueryMapTypeGuards[type].query(event.data);
}
};
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const isIframeQueryWrapper = (event: any): event is IframeQueryWrapper<keyof IframeQueryMap> => typeof event.id === 'number' && isIframeQuery(event.query);
export const isIframeQueryWrapper = (event: any): event is IframeQueryWrapper<keyof IframeQueryMap> =>
typeof event.id === "number" && isIframeQuery(event.query);
export interface IframeAnswerEvent<T extends keyof IframeQueryMap> {
id: number;
type: T;
data: IframeQueryMap[T]['answer'];
data: IframeQueryMap[T]["answer"];
}
export const isIframeAnswerEvent = (event: { type?: string, id?: number }): event is IframeAnswerEvent<keyof IframeQueryMap> => typeof event.type === 'string' && typeof event.id === 'number';
export const isIframeAnswerEvent = (event: {
type?: string;
id?: number;
}): event is IframeAnswerEvent<keyof IframeQueryMap> => typeof event.type === "string" && typeof event.id === "number";
export interface IframeErrorAnswerEvent {
id: number;
@ -157,4 +165,9 @@ export interface IframeErrorAnswerEvent {
error: string;
}
export const isIframeErrorAnswerEvent = (event: { type?: string, id?: number, error?: string }): event is IframeErrorAnswerEvent => typeof event.type === 'string' && typeof event.id === 'number' && typeof event.error === 'string';
export const isIframeErrorAnswerEvent = (event: {
type?: string;
id?: number;
error?: string;
}): event is IframeErrorAnswerEvent =>
typeof event.type === "string" && typeof event.id === "number" && typeof event.error === "string";

View file

@ -0,0 +1,12 @@
import * as tg from "generic-type-guard";
export const isLoadTilesetEvent = new tg.IsInterface()
.withProperties({
url: tg.isString,
})
.get();
/**
* A message sent from the iFrame to the game to add a message in the chat.
*/
export type LoadTilesetEvent = tg.GuardedType<typeof isLoadTilesetEvent>;

View file

@ -1,18 +1,20 @@
import * as tg from "generic-type-guard";
import {isMenuItemRegisterEvent} from "./ui/MenuItemRegisterEvent";
import { isMenuItemRegisterEvent } from "./ui/MenuItemRegisterEvent";
export const isSetVariableEvent =
new tg.IsInterface().withProperties({
export const isSetVariableEvent = new tg.IsInterface()
.withProperties({
key: tg.isString,
value: tg.isUnknown,
}).get();
})
.get();
/**
* A message sent from the iFrame to the game to change the value of the property of the layer
*/
export type SetVariableEvent = tg.GuardedType<typeof isSetVariableEvent>;
export const isSetVariableIframeEvent =
new tg.IsInterface().withProperties({
export const isSetVariableIframeEvent = new tg.IsInterface()
.withProperties({
type: tg.isSingletonString("setVariable"),
data: isSetVariableEvent
}).get();
data: isSetVariableEvent,
})
.get();

View file

@ -12,7 +12,8 @@ import { isOpenCoWebsite, OpenCoWebSiteEvent } from "./Events/OpenCoWebSiteEvent
import {
IframeErrorAnswerEvent,
IframeEvent,
IframeEventMap, IframeQueryMap,
IframeEventMap,
IframeQueryMap,
IframeResponseEvent,
IframeResponseEventMap,
isIframeEventWrapper,
@ -25,16 +26,16 @@ import { isStopSoundEvent, StopSoundEvent } from "./Events/StopSoundEvent";
import { isLoadSoundEvent, LoadSoundEvent } from "./Events/LoadSoundEvent";
import { isSetPropertyEvent, SetPropertyEvent } from "./Events/setPropertyEvent";
import { isLayerEvent, LayerEvent } from "./Events/LayerEvent";
import { isMenuItemRegisterEvent } from "./Events/ui/MenuItemRegisterEvent";
import type { MapDataEvent } from "./Events/MapDataEvent";
import type { GameStateEvent } from "./Events/GameStateEvent";
import type { HasPlayerMovedEvent } from "./Events/HasPlayerMovedEvent";
import { isLoadPageEvent } from "./Events/LoadPageEvent";
import { handleMenuItemRegistrationEvent, isMenuItemRegisterIframeEvent } from "./Events/ui/MenuItemRegisterEvent";
import { SetTilesEvent, isSetTilesEvent } from "./Events/SetTilesEvent";
import type { SetVariableEvent } from "./Events/SetVariableEvent";
type AnswererCallback<T extends keyof IframeQueryMap> = (query: IframeQueryMap[T]["query"], source: MessageEventSource | null) => IframeQueryMap[T]["answer"] | PromiseLike<IframeQueryMap[T]["answer"]>;
type AnswererCallback<T extends keyof IframeQueryMap> = (
query: IframeQueryMap[T]["query"],
source: MessageEventSource | null
) => IframeQueryMap[T]["answer"] | PromiseLike<IframeQueryMap[T]["answer"]>;
/**
* Listens to messages from iframes and turn those messages into easy to use observables.
@ -112,13 +113,11 @@ class IframeListener {
private readonly scripts = new Map<string, HTMLIFrameElement>();
private sendPlayerMove: boolean = false;
// Note: we are forced to type this in unknown and later cast with "as" because of https://github.com/microsoft/TypeScript/issues/31904
private answerers: {
[str in keyof IframeQueryMap]?: unknown
[str in keyof IframeQueryMap]?: unknown;
} = {};
init() {
window.addEventListener(
"message",
@ -158,42 +157,56 @@ class IframeListener {
const answerer = this.answerers[query.type] as AnswererCallback<keyof IframeQueryMap> | undefined;
if (answerer === undefined) {
const errorMsg = 'The iFrame sent a message of type "'+query.type+'" but there is no service configured to answer these messages.';
const errorMsg =
'The iFrame sent a message of type "' +
query.type +
'" but there is no service configured to answer these messages.';
console.error(errorMsg);
iframe.contentWindow?.postMessage({
id: queryId,
type: query.type,
error: errorMsg
} as IframeErrorAnswerEvent, '*');
iframe.contentWindow?.postMessage(
{
id: queryId,
type: query.type,
error: errorMsg,
} as IframeErrorAnswerEvent,
"*"
);
return;
}
const errorHandler = (reason: unknown) => {
console.error('An error occurred while responding to an iFrame query.', reason);
let reasonMsg: string = '';
console.error("An error occurred while responding to an iFrame query.", reason);
let reasonMsg: string = "";
if (reason instanceof Error) {
reasonMsg = reason.message;
} else if (typeof reason === 'object') {
reasonMsg = reason ? reason.toString() : '';
} else if (typeof reason === 'string') {
} else if (typeof reason === "object") {
reasonMsg = reason ? reason.toString() : "";
} else if (typeof reason === "string") {
reasonMsg = reason;
}
iframe?.contentWindow?.postMessage({
id: queryId,
type: query.type,
error: reasonMsg
} as IframeErrorAnswerEvent, '*');
iframe?.contentWindow?.postMessage(
{
id: queryId,
type: query.type,
error: reasonMsg,
} as IframeErrorAnswerEvent,
"*"
);
};
try {
Promise.resolve(answerer(query.data, message.source)).then((value) => {
iframe?.contentWindow?.postMessage({
id: queryId,
type: query.type,
data: value
}, '*');
}).catch(errorHandler);
Promise.resolve(answerer(query.data, message.source))
.then((value) => {
iframe?.contentWindow?.postMessage(
{
id: queryId,
type: query.type,
data: value,
},
"*"
);
})
.catch(errorHandler);
} catch (reason) {
errorHandler(reason);
}
@ -238,7 +251,7 @@ class IframeListener {
} else if (payload.type === "displayBubble") {
this._displayBubbleStream.next();
} else if (payload.type === "removeBubble") {
this._removeBubbleStream.next();
this._removeBubbleStream.next();
} else if (payload.type == "onPlayerMove") {
this.sendPlayerMove = true;
} else if (isMenuItemRegisterIframeEvent(payload)) {
@ -398,8 +411,8 @@ class IframeListener {
setVariable(setVariableEvent: SetVariableEvent) {
this.postMessage({
'type': 'setVariable',
'data': setVariableEvent
type: "setVariable",
data: setVariableEvent,
});
}
@ -420,7 +433,7 @@ class IframeListener {
* @param key The "type" of the query we are answering
* @param callback
*/
public registerAnswerer<T extends keyof IframeQueryMap>(key: T, callback: AnswererCallback<T> ): void {
public registerAnswerer<T extends keyof IframeQueryMap>(key: T, callback: AnswererCallback<T>): void {
this.answerers[key] = callback;
}
@ -432,13 +445,16 @@ class IframeListener {
// Let's dispatch the message to the other iframes
for (const iframe of this.iframes) {
if (iframe.contentWindow !== source) {
iframe.contentWindow?.postMessage({
'type': 'setVariable',
'data': {
key,
value,
}
}, '*');
iframe.contentWindow?.postMessage(
{
type: "setVariable",
data: {
key,
value,
},
},
"*"
);
}
}
}

View file

@ -1,4 +1,4 @@
import { Observable, Subject } from "rxjs";
import { Subject } from "rxjs";
import { EnterLeaveEvent, isEnterLeaveEvent } from "../Events/EnterLeaveEvent";
@ -105,6 +105,14 @@ export class WorkadventureRoomCommands extends IframeApiContribution<Workadventu
}
return mapURL;
}
async loadTileset(url: string): Promise<number> {
return await queryWorkadventure({
type: "loadTileset",
data: {
url: url,
},
});
}
}
export default new WorkadventureRoomCommands();

View file

@ -27,6 +27,10 @@
import {gameOverlayVisibilityStore} from "../Stores/GameOverlayStoreVisibility";
import {consoleGlobalMessageManagerVisibleStore} from "../Stores/ConsoleGlobalMessageManagerStore";
import ConsoleGlobalMessageManager from "./ConsoleGlobalMessageManager/ConsoleGlobalMessageManager.svelte";
import AdminMessage from "./TypeMessage/BanMessage.svelte";
import TextMessage from "./TypeMessage/TextMessage.svelte";
import {banMessageVisibleStore} from "../Stores/TypeMessageStore/BanMessageStore";
import {textMessageVisibleStore} from "../Stores/TypeMessageStore/TextMessageStore";
import {warningContainerStore} from "../Stores/MenuStore";
import WarningContainer from "./WarningContainer/WarningContainer.svelte";
import {layoutManagerVisibilityStore} from "../Stores/LayoutManagerStore";
@ -62,6 +66,16 @@
<EnableCameraScene game={game}></EnableCameraScene>
</div>
{/if}
{#if $banMessageVisibleStore}
<div>
<AdminMessage></AdminMessage>
</div>
{/if}
{#if $textMessageVisibleStore}
<div>
<TextMessage></TextMessage>
</div>
{/if}
{#if $soundPlayingStore}
<div>
<AudioPlaying url={$soundPlayingStore} />

View file

@ -9,8 +9,8 @@
export let game: Game;
let inputSendTextActive = true;
let uploadMusicActive = false;
let handleSendText: { sendTextMessage(): void };
let handleSendAudio: { sendAudioMessage(): Promise<void> };
let handleSendText: { sendTextMessage(broadcast: boolean): void };
let handleSendAudio: { sendAudioMessage(broadcast: boolean): Promise<void> };
let broadcastToWorld = false;
function closeConsoleGlobalMessage() {
@ -35,10 +35,10 @@
function send() {
if (inputSendTextActive) {
handleSendText.sendTextMessage();
handleSendText.sendTextMessage(broadcastToWorld);
}
if (uploadMusicActive) {
handleSendAudio.sendAudioMessage();
handleSendAudio.sendAudioMessage(broadcastToWorld);
}
}
</script>

View file

@ -1,11 +1,11 @@
<script lang="ts">
import { consoleGlobalMessageManagerFocusStore, consoleGlobalMessageManagerVisibleStore } from "../../Stores/ConsoleGlobalMessageManagerStore";
import { onMount } from "svelte";
import {onDestroy, onMount} from "svelte";
import type { Game } from "../../Phaser/Game/Game";
import type { GameManager } from "../../Phaser/Game/GameManager";
import type { PlayGlobalMessageInterface } from "../../Connexion/ConnexionModels";
import { AdminMessageEventTypes } from "../../Connexion/AdminMessagesService";
import type { Quill } from "quill";
import type { PlayGlobalMessageInterface } from "../../Connexion/ConnexionModels";
//toolbar
const toolbarOptions = [
@ -34,27 +34,27 @@
export let game: Game;
export let gameManager: GameManager;
let gameScene = gameManager.getCurrentGameScene(game.findAnyScene());
const gameScene = gameManager.getCurrentGameScene(game.findAnyScene());
let quill: Quill;
let INPUT_CONSOLE_MESSAGE: HTMLDivElement;
const MESSAGE_TYPE = AdminMessageEventTypes.admin;
export const handleSending = {
sendTextMessage() {
sendTextMessage(broadcastToWorld: boolean) {
if (gameScene == undefined) {
return;
}
const text = quill.getText(0, quill.getLength());
const text = JSON.stringify(quill.getContents(0, quill.getLength()));
const GlobalMessage: PlayGlobalMessageInterface = {
id: "1", // FIXME: use another ID?
message: text,
type: MESSAGE_TYPE
const textGlobalMessage: PlayGlobalMessageInterface = {
type: MESSAGE_TYPE,
content: text,
broadcastToWorld: broadcastToWorld
};
quill.deleteText(0, quill.getLength());
gameScene.connection?.emitGlobalMessage(GlobalMessage);
gameScene.connection?.emitGlobalMessage(textGlobalMessage);
disableConsole();
}
}
@ -73,14 +73,13 @@
},
});
quill.on('selection-change', function (range, oldRange) {
if (range === null && oldRange !== null) {
consoleGlobalMessageManagerFocusStore.set(false);
} else if (range !== null && oldRange === null)
consoleGlobalMessageManagerFocusStore.set(true);
});
consoleGlobalMessageManagerFocusStore.set(true);
});
onDestroy(() => {
consoleGlobalMessageManagerFocusStore.set(false);
})
function disableConsole() {
consoleGlobalMessageManagerVisibleStore.set(false);
consoleGlobalMessageManagerFocusStore.set(false);

View file

@ -4,8 +4,8 @@
import type { GameManager } from "../../Phaser/Game/GameManager";
import { consoleGlobalMessageManagerFocusStore, consoleGlobalMessageManagerVisibleStore } from "../../Stores/ConsoleGlobalMessageManagerStore";
import { AdminMessageEventTypes } from "../../Connexion/AdminMessagesService";
import type { PlayGlobalMessageInterface } from "../../Connexion/ConnexionModels";
import uploadFile from "../images/music-file.svg";
import type {PlayGlobalMessageInterface} from "../../Connexion/ConnexionModels";
interface EventTargetFiles extends EventTarget {
files: Array<File>;
@ -23,7 +23,7 @@
const AUDIO_TYPE = AdminMessageEventTypes.audio;
export const handleSending = {
async sendAudioMessage() {
async sendAudioMessage(broadcast: boolean) {
if (gameScene == undefined) {
return;
}
@ -38,13 +38,13 @@
fd.append('file', selectedFile);
const res = await gameScene.connection?.uploadAudio(fd);
const GlobalMessage: PlayGlobalMessageInterface = {
id: (res as { id: string }).id,
message: (res as { path: string }).path,
type: AUDIO_TYPE
const audioGlobalMessage: PlayGlobalMessageInterface = {
content: (res as { path: string }).path,
type: AUDIO_TYPE,
broadcastToWorld: broadcast
}
inputAudio.value = '';
gameScene.connection?.emitGlobalMessage(GlobalMessage);
gameScene.connection?.emitGlobalMessage(audioGlobalMessage);
disableConsole();
}
}

View file

@ -0,0 +1,96 @@
<script lang="ts">
import { fly } from "svelte/transition";
import {banMessageVisibleStore, banMessageContentStore} from "../../Stores/TypeMessageStore/BanMessageStore";
import {onMount} from "svelte";
const text = $banMessageContentStore;
const NAME_BUTTON = 'Ok';
let nbSeconds = 10;
let nameButton = '';
onMount(() => {
timeToRead()
})
function timeToRead() {
nbSeconds -= 1;
nameButton = nbSeconds.toString();
if ( nbSeconds > 0 ) {
setTimeout( () => {
timeToRead();
}, 1000);
} else {
nameButton = NAME_BUTTON;
}
}
function closeBanMessage() {
banMessageVisibleStore.set(false);
}
</script>
<div class="main-ban-message nes-container is-rounded" transition:fly="{{ y: -1000, duration: 500 }}">
<h2 class="title-ban-message"><img src="resources/logos/report.svg" alt="***"/> Important message <img src="resources/logos/report.svg" alt="***"/></h2>
<div class="content-ban-message">
<p>{text}</p>
</div>
<div class="footer-ban-message">
<button type="button" class="nes-btn {nameButton === NAME_BUTTON ? 'is-primary' : 'is-error'}" disabled="{!(nameButton === NAME_BUTTON)}" on:click|preventDefault={closeBanMessage}>{nameButton}</button>
</div>
<audio id="report-message" autoplay>
<source src="/resources/objects/report-message.mp3" type="audio/mp3">
</audio>
</div>
<style lang="scss">
div.main-ban-message {
display: flex;
flex-direction: column;
position: relative;
top: 15vh;
height: 70vh;
width: 60vw;
margin-left: auto;
margin-right: auto;
padding-bottom: 0;
pointer-events: auto;
background-color: #333333;
color: whitesmoke;
h2.title-ban-message {
flex: 1 1 auto;
max-height: 50px;
margin-bottom: 20px;
text-align: center;
img {
height: 50px;
}
}
div.content-ban-message {
flex: 1 1 auto;
max-height: calc(100% - 50px);
overflow: auto;
p {
white-space: pre-wrap;
}
}
div.footer-ban-message {
height: 50px;
margin-top: 10px;
text-align: center;
button {
width: 88px;
height: 44px;
}
}
}
</style>

View file

@ -0,0 +1,59 @@
<script lang="ts">
import { fly } from "svelte/transition";
import {textMessageContentStore, textMessageVisibleStore} from "../../Stores/TypeMessageStore/TextMessageStore";
import { QuillDeltaToHtmlConverter } from "quill-delta-to-html";
const content = JSON.parse($textMessageContentStore);
const converter = new QuillDeltaToHtmlConverter(content.ops, {inlineStyles: true});
const NAME_BUTTON = 'Ok';
function closeTextMessage() {
textMessageVisibleStore.set(false);
}
function onKeyDown(e:KeyboardEvent) {
if (e.key === 'Escape') {
closeTextMessage();
}
}
</script>
<svelte:window on:keydown={onKeyDown}/>
<div class="main-text-message nes-container is-rounded" transition:fly="{{ x: -1000, duration: 500 }}">
<div class="content-text-message">
{@html converter.convert()}
</div>
<div class="footer-text-message">
<button type="button" class="nes-btn is-primary" on:click|preventDefault={closeTextMessage}>{NAME_BUTTON}</button>
</div>
</div>
<style lang="scss">
div.main-text-message {
display: flex;
flex-direction: column;
max-height: 25vh;
width: 80vw;
margin-right: auto;
margin-left: auto;
padding-bottom: 0;
pointer-events: auto;
background-color: #333333;
div.content-text-message {
flex: 1 1 auto;
max-height: calc(100% - 50px);
color: whitesmoke;
overflow: auto;
}
div.footer-text-message {
height: 50px;
text-align: center;
}
}
</style>

View file

@ -64,11 +64,8 @@ class ConnectionManager {
);
localUserStore.setAuthToken(authToken);
this.authToken = authToken;
room = await Room.createRoom(
new URL(localUserStore.getLastRoomUrl())
);
room = await Room.createRoom(new URL(localUserStore.getLastRoomUrl()));
urlManager.pushRoomIdToUrl(room);
} else if (connexionType === GameConnexionTypes.register) {
//@deprecated
const organizationMemberToken = urlManager.getOrganizationToken();

View file

@ -110,9 +110,9 @@ export interface RoomJoinedMessageInterface {
}
export interface PlayGlobalMessageInterface {
id: string;
type: string;
message: string;
content: string;
broadcastToWorld: boolean;
}
export interface OnConnectInterface {

View file

@ -597,7 +597,7 @@ export class RoomConnection implements RoomConnection {
});
}
public receivePlayGlobalMessage(callback: (message: PlayGlobalMessageInterface) => void) {
/* public receivePlayGlobalMessage(callback: (message: PlayGlobalMessageInterface) => void) {
return this.onMessage(EventMessage.PLAY_GLOBAL_MESSAGE, (message: PlayGlobalMessage) => {
callback({
id: message.getId(),
@ -605,7 +605,7 @@ export class RoomConnection implements RoomConnection {
message: message.getMessage(),
});
});
}
}*/
public receiveStopGlobalMessage(callback: (messageId: string) => void) {
return this.onMessage(EventMessage.STOP_GLOBAL_MESSAGE, (message: StopGlobalMessage) => {
@ -619,11 +619,11 @@ export class RoomConnection implements RoomConnection {
});
}
public emitGlobalMessage(message: PlayGlobalMessageInterface) {
public emitGlobalMessage(message: PlayGlobalMessageInterface): void {
const playGlobalMessage = new PlayGlobalMessage();
playGlobalMessage.setId(message.id);
playGlobalMessage.setType(message.type);
playGlobalMessage.setMessage(message.message);
playGlobalMessage.setContent(message.content);
playGlobalMessage.setBroadcasttoworld(message.broadcastToWorld);
const clientToServerMessage = new ClientToServerMessage();
clientToServerMessage.setPlayglobalmessage(playGlobalMessage);

View file

@ -1,5 +1,4 @@
import type { Subscription } from "rxjs";
import { GlobalMessageManager } from "../../Administration/GlobalMessageManager";
import { userMessageManager } from "../../Administration/UserMessageManager";
import { iframeListener } from "../../Api/IframeListener";
import { connectionManager } from "../../Connexion/ConnectionManager";
@ -74,8 +73,6 @@ import { joystickBaseImg, joystickBaseKey, joystickThumbImg, joystickThumbKey }
import { waScaleManager } from "../Services/WaScaleManager";
import { EmoteManager } from "./EmoteManager";
import EVENT_TYPE = Phaser.Scenes.Events;
import RenderTexture = Phaser.GameObjects.RenderTexture;
import Tilemap = Phaser.Tilemaps.Tilemap;
import type { HasPlayerMovedEvent } from "../../Api/Events/HasPlayerMovedEvent";
import AnimatedTiles from "phaser-animated-tiles";
@ -87,6 +84,7 @@ import { biggestAvailableAreaStore } from "../../Stores/BiggestAvailableAreaStor
import { SharedVariablesManager } from "./SharedVariablesManager";
import { playersStore } from "../../Stores/PlayersStore";
import { chatVisibilityStore } from "../../Stores/ChatStore";
import Tileset = Phaser.Tilemaps.Tileset;
import { userIsAdminStore } from "../../Stores/GameStore";
import { layoutManagerActionStore, layoutManagerVisibilityStore } from "../../Stores/LayoutManagerStore";
import { get } from "svelte/store";
@ -157,7 +155,6 @@ export class GameScene extends DirtyScene {
private playersPositionInterpolator = new PlayersPositionInterpolator();
public connection: RoomConnection | undefined;
private simplePeer!: SimplePeer;
private GlobalMessageManager!: GlobalMessageManager;
private connectionAnswerPromise: Promise<RoomJoinedMessageInterface>;
private connectionAnswerPromiseResolve!: (
value: RoomJoinedMessageInterface | PromiseLike<RoomJoinedMessageInterface>
@ -223,6 +220,9 @@ export class GameScene extends DirtyScene {
//hook preload scene
preload(): void {
//initialize frame event of scripting API
this.listenToIframeEvents();
const localUser = localUserStore.getLocalUser();
const textures = localUser?.textures;
if (textures) {
@ -551,7 +551,6 @@ export class GameScene extends DirtyScene {
);
this.triggerOnMapLayerPropertyChange();
this.listenToIframeEvents();
if (!this.room.isDisconnected()) {
this.connect();
@ -694,7 +693,6 @@ export class GameScene extends DirtyScene {
peerStore.connectToSimplePeer(this.simplePeer);
screenSharingPeerStore.connectToSimplePeer(this.simplePeer);
videoFocusStore.connectToSimplePeer(this.simplePeer);
this.GlobalMessageManager = new GlobalMessageManager(this.connection);
userMessageManager.setReceiveBanListener(this.bannedUser.bind(this));
const self = this;
@ -1083,8 +1081,74 @@ ${escapedMessage}
for (const eventTile of eventTiles) {
this.gameMap.putTile(eventTile.tile, eventTile.x, eventTile.y, eventTile.layer);
}
this.markDirty();
})
);
iframeListener.registerAnswerer("loadTileset", (eventTileset) => {
return this.connectionAnswerPromise.then(() => {
const jsonTilesetDir = eventTileset.url.substr(0, eventTileset.url.lastIndexOf("/"));
//Initialise the firstgid to 1 because if there is no tileset in the tilemap, the firstgid will be 1
let newFirstgid = 1;
const lastTileset = this.mapFile.tilesets[this.mapFile.tilesets.length - 1];
if (lastTileset) {
//If there is at least one tileset in the tilemap then calculate the firstgid of the new tileset
newFirstgid = lastTileset.firstgid + lastTileset.tilecount;
}
return new Promise((resolve, reject) => {
this.load.on("filecomplete-json-" + eventTileset.url, () => {
let jsonTileset = this.cache.json.get(eventTileset.url);
const imageUrl = jsonTilesetDir + "/" + jsonTileset.image;
this.load.image(imageUrl, imageUrl);
this.load.on("filecomplete-image-" + imageUrl, () => {
//Add the firstgid of the tileset to the json file
jsonTileset = { ...jsonTileset, firstgid: newFirstgid };
this.mapFile.tilesets.push(jsonTileset);
this.Map.tilesets.push(
new Tileset(
jsonTileset.name,
jsonTileset.firstgid,
jsonTileset.tileWidth,
jsonTileset.tileHeight,
jsonTileset.margin,
jsonTileset.spacing,
jsonTileset.tiles
)
);
this.Terrains.push(
this.Map.addTilesetImage(
jsonTileset.name,
imageUrl,
jsonTileset.tilewidth,
jsonTileset.tileheight,
jsonTileset.margin,
jsonTileset.spacing
)
);
//destroy the tilemapayer because they are unique and we need to reuse their key and layerdData
for (const layer of this.Map.layers) {
layer.tilemapLayer.destroy(false);
}
//Create a new GameMap with the changed file
this.gameMap = new GameMap(this.mapFile, this.Map, this.Terrains);
//Destroy the colliders of the old tilemapLayer
this.physics.add.world.colliders.destroy();
//Create new colliders with the new GameMap
this.createCollisionWithPlayer();
//Create new trigger with the new GameMap
this.triggerOnMapLayerPropertyChange();
resolve(newFirstgid);
});
});
this.load.on("loaderror", () => {
console.error("Error while loading " + eventTileset.url + ".");
reject(-1);
});
this.load.json(eventTileset.url, eventTileset.url);
this.load.start();
});
});
});
}
private setPropertyLayer(
@ -1206,6 +1270,7 @@ ${escapedMessage}
this.chatVisibilityUnsubscribe();
this.biggestAvailableAreaStoreUnsubscribe();
iframeListener.unregisterAnswerer("getState");
iframeListener.unregisterAnswerer("loadTileset");
this.sharedVariablesManager?.close();
mediaManager.hideGameOverlay();

View file

@ -387,7 +387,11 @@ export class MenuScene extends Phaser.Scene {
private gotToCreateMapPage() {
//const sparkHost = 'https://'+window.location.host.replace('play.', '')+'/choose-map.html';
//TODO fix me: this button can to send us on WorkAdventure BO.
const sparkHost = ADMIN_URL + "/getting-started";
//const sparkHost = ADMIN_URL + "/getting-started";
//The redirection must be only on workadventu.re domain
//To day the domain staging cannot be use by customer
const sparkHost = "https://workadventu.re/getting-started";
window.open(sparkHost, "_blank");
}

View file

@ -1,12 +1,12 @@
import type { Direction } from "../../types";
import type {GameScene} from "../Game/GameScene";
import {touchScreenManager} from "../../Touch/TouchScreenManager";
import {MobileJoystick} from "../Components/MobileJoystick";
import {enableUserInputsStore} from "../../Stores/UserInputStore";
import type { GameScene } from "../Game/GameScene";
import { touchScreenManager } from "../../Touch/TouchScreenManager";
import { MobileJoystick } from "../Components/MobileJoystick";
import { enableUserInputsStore } from "../../Stores/UserInputStore";
interface UserInputManagerDatum {
keyInstance: Phaser.Input.Keyboard.Key;
event: UserInputEvent
event: UserInputEvent;
}
export enum UserInputEvent {
@ -20,10 +20,9 @@ export enum UserInputEvent {
JoystickMove,
}
//we cannot use a map structure so we have to create a replacement
export class ActiveEventList {
private eventMap : Map<UserInputEvent, boolean> = new Map<UserInputEvent, boolean>();
private eventMap: Map<UserInputEvent, boolean> = new Map<UserInputEvent, boolean>();
get(event: UserInputEvent): boolean {
return this.eventMap.get(event) || false;
@ -43,7 +42,7 @@ export class ActiveEventList {
export class UserInputManager {
private KeysCode!: UserInputManagerDatum[];
private Scene: GameScene;
private isInputDisabled : boolean;
private isInputDisabled: boolean;
private joystick!: MobileJoystick;
private joystickEvents = new ActiveEventList();
@ -61,8 +60,8 @@ export class UserInputManager {
}
enableUserInputsStore.subscribe((enable) => {
enable ? this.restoreControls() : this.disableControls()
})
enable ? this.restoreControls() : this.disableControls();
});
}
initVirtualJoystick() {
@ -91,39 +90,81 @@ export class UserInputManager {
});
}
initKeyBoardEvent(){
initKeyBoardEvent() {
this.KeysCode = [
{event: UserInputEvent.MoveUp, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Z, false) },
{event: UserInputEvent.MoveUp, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.W, false) },
{event: UserInputEvent.MoveLeft, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Q, false) },
{event: UserInputEvent.MoveLeft, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A, false) },
{event: UserInputEvent.MoveDown, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S, false) },
{event: UserInputEvent.MoveRight, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D, false) },
{
event: UserInputEvent.MoveUp,
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Z, false),
},
{
event: UserInputEvent.MoveUp,
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.W, false),
},
{
event: UserInputEvent.MoveLeft,
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.Q, false),
},
{
event: UserInputEvent.MoveLeft,
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.A, false),
},
{
event: UserInputEvent.MoveDown,
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.S, false),
},
{
event: UserInputEvent.MoveRight,
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.D, false),
},
{event: UserInputEvent.MoveUp, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.UP, false) },
{event: UserInputEvent.MoveLeft, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.LEFT, false) },
{event: UserInputEvent.MoveDown, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.DOWN, false) },
{event: UserInputEvent.MoveRight, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.RIGHT, false) },
{
event: UserInputEvent.MoveUp,
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.UP, false),
},
{
event: UserInputEvent.MoveLeft,
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.LEFT, false),
},
{
event: UserInputEvent.MoveDown,
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.DOWN, false),
},
{
event: UserInputEvent.MoveRight,
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.RIGHT, false),
},
{event: UserInputEvent.SpeedUp, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SHIFT, false) },
{
event: UserInputEvent.SpeedUp,
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SHIFT, false),
},
{event: UserInputEvent.Interact, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.E, false) },
{event: UserInputEvent.Interact, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE, false) },
{event: UserInputEvent.Shout, keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.F, false) },
{
event: UserInputEvent.Interact,
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.E, false),
},
{
event: UserInputEvent.Interact,
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE, false),
},
{
event: UserInputEvent.Shout,
keyInstance: this.Scene.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.F, false),
},
];
}
clearAllListeners(){
clearAllListeners() {
this.Scene.input.keyboard.removeAllListeners();
}
//todo: should we also disable the joystick?
disableControls(){
disableControls() {
this.Scene.input.keyboard.removeAllKeys();
this.isInputDisabled = true;
}
restoreControls(){
restoreControls() {
this.initKeyBoardEvent();
this.isInputDisabled = false;
}
@ -135,27 +176,27 @@ export class UserInputManager {
this.joystickEvents.forEach((value, key) => {
if (value) {
switch (key) {
case UserInputEvent.MoveUp:
case UserInputEvent.MoveDown:
this.joystickForceAccuY += this.joystick.forceY;
if (Math.abs(this.joystickForceAccuY) > this.joystickForceThreshold) {
eventsMap.set(key, value);
this.joystickForceAccuY = 0;
}
break;
case UserInputEvent.MoveLeft:
case UserInputEvent.MoveRight:
this.joystickForceAccuX += this.joystick.forceX;
if (Math.abs(this.joystickForceAccuX) > this.joystickForceThreshold) {
eventsMap.set(key, value);
this.joystickForceAccuX = 0;
}
break;
case UserInputEvent.MoveUp:
case UserInputEvent.MoveDown:
this.joystickForceAccuY += this.joystick.forceY;
if (Math.abs(this.joystickForceAccuY) > this.joystickForceThreshold) {
eventsMap.set(key, value);
this.joystickForceAccuY = 0;
}
break;
case UserInputEvent.MoveLeft:
case UserInputEvent.MoveRight:
this.joystickForceAccuX += this.joystick.forceX;
if (Math.abs(this.joystickForceAccuX) > this.joystickForceThreshold) {
eventsMap.set(key, value);
this.joystickForceAccuX = 0;
}
break;
}
}
});
eventsMap.set(UserInputEvent.JoystickMove, this.joystickEvents.any());
this.KeysCode.forEach(d => {
this.KeysCode.forEach((d) => {
if (d.keyInstance.isDown) {
eventsMap.set(d.event, true);
}
@ -163,18 +204,18 @@ export class UserInputManager {
return eventsMap;
}
spaceEvent(callback : Function){
this.Scene.input.keyboard.on('keyup-SPACE', (event: Event) => {
spaceEvent(callback: Function) {
this.Scene.input.keyboard.on("keyup-SPACE", (event: Event) => {
callback();
return event;
});
}
addSpaceEventListner(callback : Function){
this.Scene.input.keyboard.addListener('keyup-SPACE', callback);
addSpaceEventListner(callback: Function) {
this.Scene.input.keyboard.addListener("keyup-SPACE", callback);
}
removeSpaceEventListner(callback : Function){
this.Scene.input.keyboard.removeListener('keyup-SPACE', callback);
removeSpaceEventListner(callback: Function) {
this.Scene.input.keyboard.removeListener("keyup-SPACE", callback);
}
destroy(): void {
@ -182,8 +223,11 @@ export class UserInputManager {
}
private initMouseWheel() {
this.Scene.input.on('wheel', (pointer: unknown, gameObjects: unknown, deltaX: number, deltaY: number, deltaZ: number) => {
this.Scene.zoomByFactor(1 - deltaY / 53 * 0.1);
});
this.Scene.input.on(
"wheel",
(pointer: unknown, gameObjects: unknown, deltaX: number, deltaY: number, deltaZ: number) => {
this.Scene.zoomByFactor(1 - (deltaY / 53) * 0.1);
}
);
}
}

View file

@ -0,0 +1,5 @@
import { writable } from "svelte/store";
export const banMessageVisibleStore = writable(false);
export const banMessageContentStore = writable("");

View file

@ -0,0 +1,5 @@
import { writable } from "svelte/store";
export const textMessageVisibleStore = writable(false);
export const textMessageContentStore = writable("");

View file

@ -9,8 +9,7 @@ section.section-input-send-text {
}
div.input-send-text{
height: auto;
max-height: calc(100% - 100px);
height: calc(100% - 100px);
overflow: auto;
color: whitesmoke;
@ -20,5 +19,13 @@ section.section-input-send-text {
color: whitesmoke;
font-size: 1rem;
}
.ql-tooltip {
top: 40% !important;
left: 20% !important;
color: whitesmoke;
background-color: #333333;
}
}
}

View file

@ -1,28 +1,28 @@
import "jasmine";
import {getRessourceDescriptor} from "../../../src/Phaser/Entity/PlayerTexturesLoadingManager";
import { getRessourceDescriptor } from "../../../src/Phaser/Entity/PlayerTexturesLoadingManager";
describe("getRessourceDescriptor()", () => {
it(", if given a valid descriptor as parameter, should return it", () => {
const desc = getRessourceDescriptor({name: 'name', img: 'url'});
expect(desc.name).toEqual('name');
expect(desc.img).toEqual('url');
const desc = getRessourceDescriptor({ name: "name", img: "url" });
expect(desc.name).toEqual("name");
expect(desc.img).toEqual("url");
});
it(", if given a string as parameter, should search through hardcoded values", () => {
const desc = getRessourceDescriptor('male1');
expect(desc.name).toEqual('male1');
const desc = getRessourceDescriptor("male1");
expect(desc.name).toEqual("male1");
expect(desc.img).toEqual("resources/characters/pipoya/Male 01-1.png");
});
it(", if given a string as parameter, should search through hardcoded values (bis)", () => {
const desc = getRessourceDescriptor('color_2');
expect(desc.name).toEqual('color_2');
const desc = getRessourceDescriptor("color_2");
expect(desc.name).toEqual("color_2");
expect(desc.img).toEqual("resources/customisation/character_color/character_color1.png");
});
it(", if given a descriptor without url as parameter, should search through hardcoded values", () => {
const desc = getRessourceDescriptor({name: 'male1', img: ''});
expect(desc.name).toEqual('male1');
const desc = getRessourceDescriptor({ name: "male1", img: "" });
expect(desc.name).toEqual("male1");
expect(desc.img).toEqual("resources/characters/pipoya/Male 01-1.png");
});
});
});

View file

@ -3475,6 +3475,11 @@ lodash.clonedeep@^4.5.0:
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
lodash.isequal@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.isequal/-/lodash.isequal-4.5.0.tgz#415c4478f2bcc30120c22ce10ed3226f7d3e18e0"
integrity sha1-QVxEePK8wwEgwizhDtMib30+GOA=
lodash.truncate@^4.4.2:
version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
@ -4497,6 +4502,13 @@ queue-typescript@^1.0.1:
dependencies:
linked-list-typescript "^1.0.11"
quill-delta-to-html@^0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/quill-delta-to-html/-/quill-delta-to-html-0.12.0.tgz#ee572cfa208390d99d7646795358ddb81eaea17c"
integrity sha512-Yy6U2e7ov+ZlrFbj5/GbqOBCRjyNu+vuphy0Pk+7668zIMTVHJglZY2JNa++1/zkSiqptPBmP/CpsDzC4Wznsw==
dependencies:
lodash.isequal "^4.5.0"
quill-delta@^3.6.2:
version "3.6.3"
resolved "https://registry.yarnpkg.com/quill-delta/-/quill-delta-3.6.3.tgz#b19fd2b89412301c60e1ff213d8d860eac0f1032"