From bb3b58d8bf1d4b775dc2bb5648e1392178536ee6 Mon Sep 17 00:00:00 2001
From: _Bastler <_Bastler@bstly.de>
Date: Fri, 10 Sep 2021 17:06:46 +0200
Subject: [PATCH] Twemoji Svelte
---
front/src/Components/App.svelte | 4 ++
.../src/Components/EmoteMenu/EmoteMenu.svelte | 65 +++++++++++++++++
front/src/Phaser/Components/EmoteMenu.ts | 68 ------------------
front/src/Phaser/Entity/Character.ts | 70 +++++++++----------
front/src/Phaser/Game/GameScene.ts | 63 ++++++++++-------
front/src/Phaser/Player/Player.ts | 56 +++------------
front/src/Stores/EmoteStore.ts | 4 ++
7 files changed, 156 insertions(+), 174 deletions(-)
create mode 100644 front/src/Components/EmoteMenu/EmoteMenu.svelte
delete mode 100644 front/src/Phaser/Components/EmoteMenu.ts
create mode 100644 front/src/Stores/EmoteStore.ts
diff --git a/front/src/Components/App.svelte b/front/src/Components/App.svelte
index 8b033e5f..66aa2507 100644
--- a/front/src/Components/App.svelte
+++ b/front/src/Components/App.svelte
@@ -26,6 +26,7 @@
import {soundPlayingStore} from "../Stores/SoundPlayingStore";
import ErrorDialog from "./UI/ErrorDialog.svelte";
import Menu from "./Menu/Menu.svelte";
+ import EmoteMenu from "./EmoteMenu/EmoteMenu.svelte";
import VideoOverlay from "./Video/VideoOverlay.svelte";
import {gameOverlayVisibilityStore} from "../Stores/GameOverlayStoreVisibility";
import AdminMessage from "./TypeMessage/BanMessage.svelte";
@@ -111,6 +112,9 @@
diff --git a/front/src/Components/EmoteMenu/EmoteMenu.svelte b/front/src/Components/EmoteMenu/EmoteMenu.svelte
new file mode 100644
index 00000000..1a1de281
--- /dev/null
+++ b/front/src/Components/EmoteMenu/EmoteMenu.svelte
@@ -0,0 +1,65 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/front/src/Phaser/Components/EmoteMenu.ts b/front/src/Phaser/Components/EmoteMenu.ts
deleted file mode 100644
index 93df2680..00000000
--- a/front/src/Phaser/Components/EmoteMenu.ts
+++ /dev/null
@@ -1,68 +0,0 @@
-import DOMElement = Phaser.GameObjects.DOMElement;
-import { DEPTH_UI_INDEX } from "../Game/DepthIndexes";
-import { waScaleManager } from "../Services/WaScaleManager";
-import type { UserInputManager } from "../UserInput/UserInputManager";
-import { EmojiButton } from '@joeattardi/emoji-button';
-import { HtmlUtils } from "../../WebRtc/HtmlUtils";
-
-export const EmoteMenuClickEvent = "emoteClick";
-
-export class EmoteMenu extends Phaser.GameObjects.Container {
- private resizeCallback: OmitThisParameter<() => void>;
- private container: DOMElement;
- private picker: EmojiButton;
-
- constructor(scene: Phaser.Scene, x: number, y: number, private userInputManager: UserInputManager) {
- super(scene, x, y);
- this.setDepth(DEPTH_UI_INDEX);
- this.scene.add.existing(this);
- this.container = new DOMElement(this.scene, 0, 0, "div", "", "");
- this.container.setClassName("emoji-container");
- const scalingFactor = waScaleManager.uiScalingFactor * 0.5;
- this.container.setScale(scalingFactor);
- this.add(this.container);
- const emojiContainer = HtmlUtils.querySelectorOrFail(".emoji-container");
-
- this.picker = new EmojiButton({
- rootElement: emojiContainer,
- styleProperties: {
- '--font': 'Press Start 2P'
- }
- });
-
- this.picker.on("emoji", (selection) => {
- this.emit(EmoteMenuClickEvent, selection.emoji);
- });
-
- this.picker.on("hidden", () => {
- this.userInputManager.restoreControls();
- });
-
- this.resize();
- this.resizeCallback = this.resize.bind(this);
- this.scene.scale.on(Phaser.Scale.Events.RESIZE, this.resizeCallback);
- }
-
- public isOpen(): boolean {
- return this.picker.isPickerVisible();
- }
-
- public openPicker() {
- this.userInputManager.disableControls();
- const emojiContainer = HtmlUtils.querySelectorOrFail(".emoji-container");
- this.picker.showPicker(emojiContainer);
- }
-
- public closePicker() {
- this.picker.hidePicker();
- }
-
- private resize() {
- this.setScale(waScaleManager.uiScalingFactor);
- }
-
- public destroy() {
- this.scene.scale.removeListener(Phaser.Scale.Events.RESIZE, this.resizeCallback);
- super.destroy();
- }
-}
diff --git a/front/src/Phaser/Entity/Character.ts b/front/src/Phaser/Entity/Character.ts
index c189b80a..2dfc3c90 100644
--- a/front/src/Phaser/Entity/Character.ts
+++ b/front/src/Phaser/Entity/Character.ts
@@ -291,48 +291,48 @@ export abstract class Character extends Container {
playEmote(emote: string) {
this.cancelPreviousEmote();
-
- const scalingFactor = waScaleManager.uiScalingFactor;
- const emoteY = -60;
-
+ const emoteY = -45;
this.playerName.setVisible(false);
- this.emote = new Text(this.scene, -12, 0, emote, { fontFamily: '"twemoji"', fontSize: '24px' });
+ this.emote = new Text(this.scene, -10, 0, emote, { fontFamily: '"twemoji"', fontSize: '20px' });
this.emote.setAlpha(0);
this.add(this.emote);
- this.createStartTransition(scalingFactor, emoteY);
+ this.createStartTransition(emoteY);
}
- private createStartTransition(scalingFactor: number, emoteY: number) {
- this.emoteTween = this.scene?.tweens.add({
- targets: this.emote,
- props: {
- scale: scalingFactor,
- alpha: 1,
- y: emoteY,
- },
- ease: "Power2",
- duration: 500,
- onComplete: () => {
- this.startPulseTransition(emoteY, scalingFactor);
- },
- });
+ private createStartTransition(emoteY: number) {
+ if (this.emote) {
+ this.emoteTween = this.scene?.tweens.add({
+ targets: this.emote,
+ props: {
+ alpha: 1,
+ y: emoteY,
+ },
+ ease: "Power2",
+ duration: 500,
+ onComplete: () => {
+ this.startPulseTransition(emoteY);
+ },
+ });
+ }
}
- private startPulseTransition(emoteY: number, scalingFactor: number) {
- this.emoteTween = this.scene?.tweens.add({
- targets: this.emote,
- props: {
- y: emoteY * 1.3,
- scale: scalingFactor * 1.1,
- },
- duration: 250,
- yoyo: true,
- repeat: 1,
- completeDelay: 200,
- onComplete: () => {
- this.startExitTransition(emoteY);
- },
- });
+ private startPulseTransition(emoteY: number) {
+ if (this.emote) {
+ this.emoteTween = this.scene?.tweens.add({
+ targets: this.emote,
+ props: {
+ y: emoteY * 1.3,
+ scale: this.emote.scale * 1.1,
+ },
+ duration: 250,
+ yoyo: true,
+ repeat: 1,
+ completeDelay: 200,
+ onComplete: () => {
+ this.startExitTransition(emoteY);
+ },
+ });
+ }
}
private startExitTransition(emoteY: number) {
diff --git a/front/src/Phaser/Game/GameScene.ts b/front/src/Phaser/Game/GameScene.ts
index 7c6475e9..dfa0c30d 100644
--- a/front/src/Phaser/Game/GameScene.ts
+++ b/front/src/Phaser/Game/GameScene.ts
@@ -82,6 +82,7 @@ import { biggestAvailableAreaStore } from "../../Stores/BiggestAvailableAreaStor
import { SharedVariablesManager } from "./SharedVariablesManager";
import { playersStore } from "../../Stores/PlayersStore";
import { chatVisibilityStore } from "../../Stores/ChatStore";
+import { emoteStore } from "../../Stores/EmoteStore";
import {
audioManagerFileStore,
audioManagerVisibilityStore,
@@ -94,6 +95,7 @@ import { layoutManagerActionStore } from "../../Stores/LayoutManagerStore";
import { EmbeddedWebsiteManager } from "./EmbeddedWebsiteManager";
import { GameMapPropertiesListener } from "./GameMapPropertiesListener";
import type { RadialMenuItem } from "../Components/RadialMenu";
+import { get } from "svelte/store";
export interface GameSceneInitInterface {
initPosition: PointInterface | null;
@@ -171,6 +173,7 @@ export class GameScene extends DirtyScene {
private iframeSubscriptionList!: Array
;
private peerStoreUnsubscribe!: () => void;
private chatVisibilityUnsubscribe!: () => void;
+ private emoteUnsubscribe!: () => void;
private biggestAvailableAreaStoreUnsubscribe!: () => void;
MapUrlFile: string;
roomUrl: string;
@@ -272,7 +275,7 @@ export class GameScene extends DirtyScene {
// So if we are in https, we can still try to load a HTTP local resource (can be useful for testing purposes)
// See https://developer.mozilla.org/en-US/docs/Web/Security/Secure_Contexts#when_is_a_context_considered_secure
const url = new URL(file.src);
- const host = url.host.split(":")[0];
+ const host = url.host.split(":")[ 0 ];
if (
window.location.protocol === "https:" &&
file.src === this.MapUrlFile &&
@@ -321,8 +324,8 @@ export class GameScene extends DirtyScene {
//eslint-disable-next-line @typescript-eslint/no-explicit-any
(this.load as any).rexWebFont({
custom: {
- families: ["Press Start 2P"],
- urls: ["/resources/fonts/fonts.css"],
+ families: [ "Press Start 2P" ],
+ urls: [ "/resources/fonts/fonts.css" ],
testString: "abcdefg",
},
});
@@ -368,7 +371,7 @@ export class GameScene extends DirtyScene {
}
}
- for (const [itemType, objectsOfType] of this.objectsByType) {
+ for (const [ itemType, objectsOfType ] of this.objectsByType) {
// FIXME: we would ideally need for the loader to WAIT for the import to be performed, which means writing our own loader plugin.
let itemFactory: ItemFactoryInterface;
@@ -399,7 +402,7 @@ export class GameScene extends DirtyScene {
// TODO: we should pass here a factory to create sprites (maybe?)
// Do we have a state for this object?
- const state = roomJoinedAnswer.items[object.id];
+ const state = roomJoinedAnswer.items[ object.id ];
const actionableItem = itemFactory.factory(this, object, state);
this.actionableItems.set(actionableItem.getId(), actionableItem);
@@ -614,7 +617,16 @@ export class GameScene extends DirtyScene {
this.openChatIcon.setVisible(!v);
});
- Promise.all([this.connectionAnswerPromise as Promise, ...scriptPromises]).then(() => {
+ this.emoteUnsubscribe = emoteStore.subscribe(() => {
+ const emoteKey = get(emoteStore);
+ if (emoteKey) {
+ this.CurrentPlayer?.playEmote(emoteKey)
+ this.connection?.emitEmoteEvent(emoteKey);
+ emoteStore.set(null);
+ }
+ });
+
+ Promise.all([ this.connectionAnswerPromise as Promise, ...scriptPromises ]).then(() => {
this.scene.wake();
});
}
@@ -702,8 +714,8 @@ export class GameScene extends DirtyScene {
if (item === undefined) {
console.warn(
'Received an event about object "' +
- message.itemId +
- '" but cannot find this item on the map.'
+ message.itemId +
+ '" but cannot find this item on the map.'
);
return;
}
@@ -897,15 +909,15 @@ export class GameScene extends DirtyScene {
} 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."
+ openPopupEvent.targetObject +
+ "'. The first parameter of WA.openPopup() must be the name of a rectangle object in your map."
);
return;
}
const escapedMessage = HtmlUtils.escapeHtml(openPopupEvent.message);
let html = ``;
html += escapedMessage;
- if(openPopupEvent.input) {
+ if (openPopupEvent.input) {
html += ``
}
html += `
`;
@@ -938,7 +950,7 @@ export class GameScene extends DirtyScene {
const btnId = id;
button.onclick = () => {
let inputValue = '';
- if(openPopupEvent.input) {
+ if (openPopupEvent.input) {
inputValue = HtmlUtils.getElementByIdOrFail
(`popupinput-${openPopupEvent.popupId}`).value;
}
iframeListener.sendButtonClickedEvent(openPopupEvent.popupId, btnId, openPopupEvent.input, inputValue);
@@ -1095,17 +1107,17 @@ export class GameScene extends DirtyScene {
this.iframeSubscriptionList.push(iframeListener.unregisterIFrameStream.subscribe(() => {
const allProps = this.gameMap.getCurrentProperties();
- if(allProps.get("openWebsite") == null) {
+ if (allProps.get("openWebsite") == null) {
layoutManagerActionStore.removeAction("openWebsite");
} else {
const openWebsiteFunction = () => {
coWebsiteManager.loadCoWebsite(allProps.get("openWebsite") as string, this.MapUrlFile, allProps.get('openWebsiteAllowApi') as boolean | undefined, allProps.get('openWebsitePolicy') as string | undefined);
layoutManagerActionStore.removeAction("openWebsite");
-
+
};
let message = allProps.get(WEBSITE_MESSAGE_PROPERTIES);
- if(message === undefined) {
+ if (message === undefined) {
message = 'Press SPACE or touch here to open web site';
}
@@ -1118,7 +1130,7 @@ export class GameScene extends DirtyScene {
});
}
}));
-
+
this.iframeSubscriptionList.push(iframeListener.setTilesStream.subscribe((eventTiles) => {
for (const eventTile of eventTiles) {
this.gameMap.putTile(eventTile.tile, eventTile.x, eventTile.y, eventTile.layer);
@@ -1130,7 +1142,7 @@ export class GameScene extends DirtyScene {
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];
+ 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;
@@ -1251,14 +1263,14 @@ export class GameScene extends DirtyScene {
if (phaserLayers === []) {
console.warn(
'Could not find layer with name that contains "' +
- layerName +
- '" when calling WA.hideLayer / WA.showLayer'
+ layerName +
+ '" when calling WA.hideLayer / WA.showLayer'
);
return;
}
for (let i = 0; i < phaserLayers.length; i++) {
- phaserLayers[i].setVisible(visible);
- phaserLayers[i].setCollisionByProperty({ collides: true }, visible);
+ phaserLayers[ i ].setVisible(visible);
+ phaserLayers[ i ].setCollisionByProperty({ collides: true }, visible);
}
}
this.markDirty();
@@ -1338,6 +1350,7 @@ export class GameScene extends DirtyScene {
this.emoteManager.destroy();
this.peerStoreUnsubscribe();
this.chatVisibilityUnsubscribe();
+ this.emoteUnsubscribe();
this.biggestAvailableAreaStoreUnsubscribe();
iframeListener.unregisterAnswerer("getState");
iframeListener.unregisterAnswerer("loadTileset");
@@ -1468,7 +1481,7 @@ export class GameScene extends DirtyScene {
if (pointer.wasTouch && (pointer.event as TouchEvent).touches.length > 1) {
return; //we don't want the menu to open when pinching on a touch screen.
}
-
+
this.CurrentPlayer.openOrCloseEmoteMenu();
})
this.CurrentPlayer.on(requestEmoteEventName, (emoteKey: string) => {
@@ -1856,13 +1869,13 @@ export class GameScene extends DirtyScene {
const allProps = this.gameMap.getCurrentProperties();
- if(allProps.get("jitsiRoom") === undefined) {
+ if (allProps.get("jitsiRoom") === undefined) {
layoutManagerActionStore.removeAction("jitsi");
} else {
const openJitsiRoomFunction = () => {
const roomName = jitsiFactory.getRoomName(allProps.get("jitsiRoom") as string, this.instance);
const jitsiUrl = allProps.get("jitsiUrl") as string | undefined;
- if(JITSI_PRIVATE_MODE && !jitsiUrl) {
+ if (JITSI_PRIVATE_MODE && !jitsiUrl) {
const adminTag = allProps.get("jitsiRoomAdminTag") as string | undefined;
this.connection && this.connection.emitQueryJitsiJwtMessage(roomName, adminTag);
@@ -1873,7 +1886,7 @@ export class GameScene extends DirtyScene {
}
let message = allProps.get(JITSI_MESSAGE_PROPERTIES);
- if(message === undefined) {
+ if (message === undefined) {
message = 'Press SPACE or touch here to enter Jitsi Meet room';
}
layoutManagerActionStore.addAction({
diff --git a/front/src/Phaser/Player/Player.ts b/front/src/Phaser/Player/Player.ts
index 58d3933d..bd6c261a 100644
--- a/front/src/Phaser/Player/Player.ts
+++ b/front/src/Phaser/Player/Player.ts
@@ -3,7 +3,8 @@ import type { GameScene } from "../Game/GameScene";
import { UserInputEvent, UserInputManager } from "../UserInput/UserInputManager";
import { Character } from "../Entity/Character";
import { userMovingStore } from "../../Stores/GameStore";
-import { EmoteMenu, EmoteMenuClickEvent } from "../Components/EmoteMenu";
+import { get } from "svelte/store";
+import { emoteMenuStore } from "../../Stores/EmoteStore";
export const hasMovedEventName = "hasMoved";
export const requestEmoteEventName = "requestEmote";
@@ -11,8 +12,6 @@ export const requestEmoteEventName = "requestEmote";
export class Player extends Character {
private previousDirection: string = PlayerAnimationDirections.Down;
private wasMoving: boolean = false;
- private emoteMenu: EmoteMenu | null = null;
- private updateListener: () => void;
constructor(
Scene: GameScene,
@@ -30,14 +29,6 @@ export class Player extends Character {
//the current player model should be push away by other players to prevent conflict
this.getBody().setImmovable(false);
-
- this.updateListener = () => {
- if (this.emoteMenu) {
- this.emoteMenu.x = this.x;
- this.emoteMenu.y = this.y;
- }
- };
- this.scene.events.addListener("postupdate", this.updateListener);
}
moveUser(delta: number): void {
@@ -94,43 +85,16 @@ export class Player extends Character {
return this.wasMoving;
}
+ playEmote(emote: string) {
+ super.playEmote(emote);
+ emoteMenuStore.set(false);
+ }
+
openOrCloseEmoteMenu() {
- if (!this.emoteMenu) {
- this.emoteMenu = new EmoteMenu(this.scene, this.x, this.y, this.userInputManager)
- }
-
- if (this.emoteMenu.isOpen()) {
- this.closeEmoteMenu();
+ if (get(emoteMenuStore)) {
+ emoteMenuStore.set(false);
} else {
- this.openEmoteMenu();
+ emoteMenuStore.set(true);
}
}
-
- openEmoteMenu(): void {
- this.cancelPreviousEmote();
- if (!this.emoteMenu) return;
- this.emoteMenu.openPicker();
- this.emoteMenu.on(EmoteMenuClickEvent, (emote: string) => {
- this.closeEmoteMenu();
- this.emit(requestEmoteEventName, emote);
- this.playEmote(emote);
- });
- }
-
- isSilent() {
- super.isSilent();
- }
- noSilent() {
- super.noSilent();
- }
-
- closeEmoteMenu(): void {
- if (!this.emoteMenu) return;
- this.emoteMenu.closePicker();
- }
-
- destroy() {
- this.scene.events.removeListener("postupdate", this.updateListener);
- super.destroy();
- }
}
diff --git a/front/src/Stores/EmoteStore.ts b/front/src/Stores/EmoteStore.ts
new file mode 100644
index 00000000..9f02925e
--- /dev/null
+++ b/front/src/Stores/EmoteStore.ts
@@ -0,0 +1,4 @@
+import { writable } from "svelte/store";
+
+export const emoteStore = writable(null);
+export const emoteMenuStore = writable(false);
\ No newline at end of file