commit
b33e271d2e
108 changed files with 1087 additions and 8740 deletions
|
@ -1,13 +1,12 @@
|
|||
import {POSTHOG_API_KEY, POSTHOG_URL} from "../Enum/EnvironmentVariable";
|
||||
import { POSTHOG_API_KEY, POSTHOG_URL } from "../Enum/EnvironmentVariable";
|
||||
|
||||
class AnalyticsClient {
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
private posthogPromise: Promise<any>;
|
||||
|
||||
constructor() {
|
||||
if (POSTHOG_API_KEY && POSTHOG_URL) {
|
||||
this.posthogPromise = import('posthog-js').then(({default: posthog}) => {
|
||||
this.posthogPromise = import("posthog-js").then(({ default: posthog }) => {
|
||||
posthog.init(POSTHOG_API_KEY, { api_host: POSTHOG_URL, disable_cookie: true });
|
||||
return posthog;
|
||||
});
|
||||
|
@ -17,45 +16,59 @@ class AnalyticsClient {
|
|||
}
|
||||
|
||||
identifyUser(uuid: string) {
|
||||
this.posthogPromise.then(posthog => {
|
||||
posthog.identify(uuid, { uuid, wa: true });
|
||||
}).catch();
|
||||
this.posthogPromise
|
||||
.then((posthog) => {
|
||||
posthog.identify(uuid, { uuid, wa: true });
|
||||
})
|
||||
.catch();
|
||||
}
|
||||
|
||||
loggedWithSso() {
|
||||
this.posthogPromise.then(posthog => {
|
||||
posthog.capture('wa-logged-sso');
|
||||
}).catch();
|
||||
this.posthogPromise
|
||||
.then((posthog) => {
|
||||
posthog.capture("wa-logged-sso");
|
||||
})
|
||||
.catch();
|
||||
}
|
||||
|
||||
loggedWithToken() {
|
||||
this.posthogPromise.then(posthog => {
|
||||
posthog.capture('wa-logged-token');
|
||||
}).catch();
|
||||
this.posthogPromise
|
||||
.then((posthog) => {
|
||||
posthog.capture("wa-logged-token");
|
||||
})
|
||||
.catch();
|
||||
}
|
||||
|
||||
enteredRoom(roomId: string) {
|
||||
this.posthogPromise.then(posthog => {
|
||||
posthog.capture('$pageView', {roomId});
|
||||
}).catch();
|
||||
this.posthogPromise
|
||||
.then((posthog) => {
|
||||
posthog.capture("$pageView", { roomId });
|
||||
})
|
||||
.catch();
|
||||
}
|
||||
|
||||
openedMenu() {
|
||||
this.posthogPromise.then(posthog => {
|
||||
posthog.capture('wa-opened-menu');
|
||||
}).catch();
|
||||
this.posthogPromise
|
||||
.then((posthog) => {
|
||||
posthog.capture("wa-opened-menu");
|
||||
})
|
||||
.catch();
|
||||
}
|
||||
|
||||
launchEmote(emote: string) {
|
||||
this.posthogPromise.then(posthog => {
|
||||
posthog.capture('wa-emote-launch', {emote});
|
||||
}).catch();
|
||||
this.posthogPromise
|
||||
.then((posthog) => {
|
||||
posthog.capture("wa-emote-launch", { emote });
|
||||
})
|
||||
.catch();
|
||||
}
|
||||
|
||||
enteredJitsi(roomName: string, roomId: string) {
|
||||
this.posthogPromise.then(posthog => {
|
||||
posthog.capture('wa-entered-jitsi', {roomName, roomId});
|
||||
}).catch();
|
||||
this.posthogPromise
|
||||
.then((posthog) => {
|
||||
posthog.capture("wa-entered-jitsi", { roomName, roomId });
|
||||
})
|
||||
.catch();
|
||||
}
|
||||
}
|
||||
export const analyticsClient = new AnalyticsClient();
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<script lang="typescript">
|
||||
import MenuIcon from "./Menu/MenuIcon.svelte";
|
||||
import {menuIconVisiblilityStore, menuVisiblilityStore} from "../Stores/MenuStore";
|
||||
import {emoteMenuStore} from "../Stores/EmoteStore";
|
||||
import {enableCameraSceneVisibilityStore} from "../Stores/MediaStore";
|
||||
import CameraControls from "./CameraControls.svelte";
|
||||
import MyCamera from "./MyCamera.svelte";
|
||||
|
@ -26,6 +27,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 +113,11 @@
|
|||
<Menu></Menu>
|
||||
</div>
|
||||
{/if}
|
||||
{#if $emoteMenuStore}
|
||||
<div>
|
||||
<EmoteMenu></EmoteMenu>
|
||||
</div>
|
||||
{/if}
|
||||
{#if $gameOverlayVisibilityStore}
|
||||
<div>
|
||||
<VideoOverlay></VideoOverlay>
|
||||
|
|
|
@ -23,6 +23,8 @@
|
|||
audioManagerVolumeStore.setMuted(localUserStore.getAudioPlayerMuted());
|
||||
changeVolume();
|
||||
|
||||
loadAudioSettings();
|
||||
|
||||
unsubscriberFileStore = audioManagerFileStore.subscribe(() => {
|
||||
HTMLAudioPlayer.pause();
|
||||
HTMLAudioPlayer.loop = get(audioManagerVolumeStore).loop;
|
||||
|
@ -79,6 +81,11 @@
|
|||
changeVolume();
|
||||
}
|
||||
|
||||
function loadAudioSettings() {
|
||||
audioManagerVolumeStore.setVolume(localUserStore.getAudioPlayerVolume());
|
||||
audioManagerVolumeStore.setMuted(localUserStore.getAudioPlayerMuted());
|
||||
}
|
||||
|
||||
function setVolume() {
|
||||
volume = parseFloat(audioPlayerVol.value);
|
||||
audioManagerVolumeStore.setVolume(volume);
|
||||
|
@ -88,6 +95,11 @@
|
|||
changeVolume();
|
||||
}
|
||||
|
||||
function disallowKeys() {
|
||||
audioPlayerVol.blur();
|
||||
return false;
|
||||
}
|
||||
|
||||
function setDecrease() {
|
||||
audioManagerVolumeStore.setDecreaseWhileTalking(decreaseWhileTalking);
|
||||
}
|
||||
|
@ -116,7 +128,7 @@
|
|||
</g>
|
||||
</svg>
|
||||
</span>
|
||||
<input type="range" min="0" max="1" step="0.025" bind:this={audioPlayerVol} on:change={setVolume}>
|
||||
<input type="range" min="0" max="1" step="0.025" bind:this={audioPlayerVol} on:change={setVolume} on:keydown={disallowKeys}>
|
||||
</div>
|
||||
<div class="audio-manager-reduce-conversation">
|
||||
<label>
|
||||
|
|
70
front/src/Components/EmoteMenu/EmoteMenu.svelte
Normal file
70
front/src/Components/EmoteMenu/EmoteMenu.svelte
Normal file
|
@ -0,0 +1,70 @@
|
|||
<script lang="typescript">
|
||||
|
||||
import type { Unsubscriber } from "svelte/store";
|
||||
import { emoteStore, emoteMenuStore } from "../../Stores/EmoteStore";
|
||||
import { onDestroy, onMount } from "svelte";
|
||||
import { EmojiButton } from '@joeattardi/emoji-button';
|
||||
import { isMobile } from "../../Enum/EnvironmentVariable";
|
||||
|
||||
let emojiContainer: HTMLElement;
|
||||
let picker: EmojiButton;
|
||||
|
||||
let unsubscriber: Unsubscriber | null = null;
|
||||
|
||||
onMount(() => {
|
||||
picker = new EmojiButton({
|
||||
rootElement: emojiContainer,
|
||||
styleProperties: {
|
||||
'--font': 'Press Start 2P'
|
||||
},
|
||||
emojisPerRow: isMobile() ? 6 : 8,
|
||||
autoFocusSearch: false
|
||||
});
|
||||
//the timeout is here to prevent the menu from flashing
|
||||
setTimeout(() => picker.showPicker(emojiContainer), 100);
|
||||
|
||||
picker.on("emoji", (selection) => {
|
||||
emoteStore.set(selection.emoji);
|
||||
});
|
||||
|
||||
picker.on("hidden", () => {
|
||||
emoteMenuStore.closeEmoteMenu();
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
function onKeyDown(e:KeyboardEvent) {
|
||||
if (e.key === 'Escape') {
|
||||
emoteMenuStore.closeEmoteMenu();
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
if (unsubscriber) {
|
||||
unsubscriber();
|
||||
}
|
||||
|
||||
picker.destroyPicker();
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<svelte:window on:keydown={onKeyDown}/>
|
||||
|
||||
<div class="emote-menu-container">
|
||||
<div class="emote-menu" bind:this={emojiContainer}></div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.emote-menu-container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.emote-menu {
|
||||
pointer-events: all;
|
||||
}
|
||||
</style>
|
|
@ -1,9 +1,8 @@
|
|||
<script lang="ts">
|
||||
import {CONTACT_URL} from "../../Enum/EnvironmentVariable";
|
||||
|
||||
import {contactPageStore} from "../../Stores/MenuStore";
|
||||
</script>
|
||||
|
||||
<iframe title="contact" src="{CONTACT_URL}"></iframe>
|
||||
<iframe title="contact" src="{$contactPageStore}" allow="clipboard-read; clipboard-write self {$contactPageStore}" allowfullscreen></iframe>
|
||||
|
||||
<style lang="scss">
|
||||
iframe {
|
||||
|
|
|
@ -46,8 +46,11 @@ class ConnectionManager {
|
|||
loginSceneVisibleIframeStore.set(false);
|
||||
return null;
|
||||
}
|
||||
const redirectUrl = `${this._currentRoom.iframeAuthentication}?state=${state}&nonce=${nonce}&playUri=${this._currentRoom.key}`;
|
||||
window.location.assign(redirectUrl);
|
||||
const redirectUrl = new URL(`${this._currentRoom.iframeAuthentication}`);
|
||||
redirectUrl.searchParams.append("state", state);
|
||||
redirectUrl.searchParams.append("nonce", nonce);
|
||||
redirectUrl.searchParams.append("playUri", this._currentRoom.key);
|
||||
window.location.assign(redirectUrl.toString());
|
||||
return redirectUrl;
|
||||
}
|
||||
|
||||
|
|
|
@ -2,15 +2,15 @@ import { Subject } from "rxjs";
|
|||
|
||||
interface EmoteEvent {
|
||||
userId: number;
|
||||
emoteName: string;
|
||||
emote: string;
|
||||
}
|
||||
|
||||
class EmoteEventStream {
|
||||
private _stream: Subject<EmoteEvent> = new Subject();
|
||||
public stream = this._stream.asObservable();
|
||||
|
||||
fire(userId: number, emoteName: string) {
|
||||
this._stream.next({ userId, emoteName });
|
||||
fire(userId: number, emote: string) {
|
||||
this._stream.next({ userId, emote });
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import Axios from "axios";
|
||||
import { PUSHER_URL } from "../Enum/EnvironmentVariable";
|
||||
import { CONTACT_URL, PUSHER_URL } from "../Enum/EnvironmentVariable";
|
||||
import type { CharacterTexture } from "./LocalUser";
|
||||
import { localUserStore } from "./LocalUserStore";
|
||||
|
||||
|
@ -20,6 +20,7 @@ export class Room {
|
|||
private _textures: CharacterTexture[] | undefined;
|
||||
private instance: string | undefined;
|
||||
private readonly _search: URLSearchParams;
|
||||
private _contactPage: string | undefined;
|
||||
|
||||
private constructor(private roomUrl: URL) {
|
||||
this.id = roomUrl.pathname;
|
||||
|
@ -105,6 +106,7 @@ export class Room {
|
|||
this._textures = data.textures;
|
||||
this._authenticationMandatory = data.authenticationMandatory || false;
|
||||
this._iframeAuthentication = data.iframeAuthentication;
|
||||
this._contactPage = data.contactPage || CONTACT_URL;
|
||||
return new MapDetail(data.mapUrl, data.textures);
|
||||
}
|
||||
|
||||
|
@ -198,4 +200,8 @@ export class Room {
|
|||
get iframeAuthentication(): string | undefined {
|
||||
return this._iframeAuthentication;
|
||||
}
|
||||
|
||||
get contactPage(): string | undefined {
|
||||
return this._contactPage;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ export const DISPLAY_TERMS_OF_USE = process.env.DISPLAY_TERMS_OF_USE == "true";
|
|||
export const NODE_ENV = process.env.NODE_ENV || "development";
|
||||
export const CONTACT_URL = process.env.CONTACT_URL || undefined;
|
||||
export const PROFILE_URL = process.env.PROFILE_URL || undefined;
|
||||
export const POSTHOG_API_KEY: string = process.env.POSTHOG_API_KEY as string || '';
|
||||
export const POSTHOG_API_KEY: string = (process.env.POSTHOG_API_KEY as string) || "";
|
||||
export const POSTHOG_URL = process.env.POSTHOG_URL || undefined;
|
||||
|
||||
export const isMobile = (): boolean => window.innerWidth <= 800 || window.innerHeight <= 600;
|
||||
|
|
|
@ -10,6 +10,7 @@ import { DEPTH_INGAME_TEXT_INDEX } from "../Game/DepthIndexes";
|
|||
import { waScaleManager } from "../Services/WaScaleManager";
|
||||
import type OutlinePipelinePlugin from "phaser3-rex-plugins/plugins/outlinepipeline-plugin.js";
|
||||
import { isSilentStore } from "../../Stores/MediaStore";
|
||||
import { lazyLoadPlayerCharacterTextures } from "./PlayerTexturesLoadingManager";
|
||||
|
||||
const playerNameY = -25;
|
||||
|
||||
|
@ -32,7 +33,7 @@ export abstract class Character extends Container {
|
|||
//private teleportation: Sprite;
|
||||
private invisible: boolean;
|
||||
public companion?: Companion;
|
||||
private emote: Phaser.GameObjects.Sprite | null = null;
|
||||
private emote: Phaser.GameObjects.Text | null = null;
|
||||
private emoteTween: Phaser.Tweens.Tween | null = null;
|
||||
scene: GameScene;
|
||||
|
||||
|
@ -57,10 +58,17 @@ export abstract class Character extends Container {
|
|||
this.sprites = new Map<string, Sprite>();
|
||||
|
||||
//textures are inside a Promise in case they need to be lazyloaded before use.
|
||||
texturesPromise.then((textures) => {
|
||||
this.addTextures(textures, frame);
|
||||
this.invisible = false;
|
||||
});
|
||||
texturesPromise
|
||||
.then((textures) => {
|
||||
this.addTextures(textures, frame);
|
||||
this.invisible = false;
|
||||
})
|
||||
.catch(() => {
|
||||
return lazyLoadPlayerCharacterTextures(scene.load, ["color_22", "eyes_23"]).then((textures) => {
|
||||
this.addTextures(textures, frame);
|
||||
this.invisible = false;
|
||||
});
|
||||
});
|
||||
|
||||
this.playerName = new Text(scene, 0, playerNameY, name, {
|
||||
fontFamily: '"Press Start 2P"',
|
||||
|
@ -289,53 +297,48 @@ export abstract class Character extends Container {
|
|||
isSilentStore.set(false);
|
||||
}
|
||||
|
||||
playEmote(emoteKey: string) {
|
||||
playEmote(emote: string) {
|
||||
this.cancelPreviousEmote();
|
||||
|
||||
const scalingFactor = waScaleManager.uiScalingFactor * 0.05;
|
||||
const emoteY = -30 - scalingFactor * 10;
|
||||
|
||||
const emoteY = -45;
|
||||
this.playerName.setVisible(false);
|
||||
this.emote = new Sprite(this.scene, 0, 0, emoteKey);
|
||||
this.emote = new Text(this.scene, -10, 0, emote, { fontSize: "20px" });
|
||||
this.emote.setAlpha(0);
|
||||
this.emote.setScale(0.1 * scalingFactor);
|
||||
this.add(this.emote);
|
||||
this.scene.sys.updateList.add(this.emote);
|
||||
|
||||
this.createStartTransition(scalingFactor, emoteY);
|
||||
this.createStartTransition(emoteY);
|
||||
}
|
||||
|
||||
private createStartTransition(scalingFactor: number, emoteY: number) {
|
||||
private createStartTransition(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);
|
||||
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) {
|
||||
|
|
|
@ -1,24 +1,7 @@
|
|||
import type { BodyResourceDescriptionInterface } from "../Entity/PlayerTextures";
|
||||
import { emoteEventStream } from "../../Connexion/EmoteEventStream";
|
||||
import type { GameScene } from "./GameScene";
|
||||
import type { RadialMenuItem } from "../Components/RadialMenu";
|
||||
import LoaderPlugin = Phaser.Loader.LoaderPlugin;
|
||||
import type { Subscription } from "rxjs";
|
||||
|
||||
interface RegisteredEmote extends BodyResourceDescriptionInterface {
|
||||
name: string;
|
||||
img: string;
|
||||
}
|
||||
|
||||
export const emotes: { [key: string]: RegisteredEmote } = {
|
||||
"emote-heart": { name: "emote-heart", img: "resources/emotes/heart-emote.png" },
|
||||
"emote-clap": { name: "emote-clap", img: "resources/emotes/clap-emote.png" },
|
||||
"emote-hand": { name: "emote-hand", img: "resources/emotes/hand-emote.png" },
|
||||
"emote-thanks": { name: "emote-thanks", img: "resources/emotes/thanks-emote.png" },
|
||||
"emote-thumb-up": { name: "emote-thumb-up", img: "resources/emotes/thumb-up-emote.png" },
|
||||
"emote-thumb-down": { name: "emote-thumb-down", img: "resources/emotes/thumb-down-emote.png" },
|
||||
};
|
||||
|
||||
export class EmoteManager {
|
||||
private subscription: Subscription;
|
||||
|
||||
|
@ -26,47 +9,10 @@ export class EmoteManager {
|
|||
this.subscription = emoteEventStream.stream.subscribe((event) => {
|
||||
const actor = this.scene.MapPlayersByKey.get(event.userId);
|
||||
if (actor) {
|
||||
this.lazyLoadEmoteTexture(event.emoteName).then((emoteKey) => {
|
||||
actor.playEmote(emoteKey);
|
||||
});
|
||||
actor.playEmote(event.emote);
|
||||
}
|
||||
});
|
||||
}
|
||||
createLoadingPromise(loadPlugin: LoaderPlugin, playerResourceDescriptor: BodyResourceDescriptionInterface) {
|
||||
return new Promise<string>((res) => {
|
||||
if (loadPlugin.textureManager.exists(playerResourceDescriptor.name)) {
|
||||
return res(playerResourceDescriptor.name);
|
||||
}
|
||||
loadPlugin.image(playerResourceDescriptor.name, playerResourceDescriptor.img);
|
||||
loadPlugin.once("filecomplete-image-" + playerResourceDescriptor.name, () =>
|
||||
res(playerResourceDescriptor.name)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
lazyLoadEmoteTexture(textureKey: string): Promise<string> {
|
||||
const emoteDescriptor = emotes[textureKey];
|
||||
if (emoteDescriptor === undefined) {
|
||||
throw "Emote not found!";
|
||||
}
|
||||
const loadPromise = this.createLoadingPromise(this.scene.load, emoteDescriptor);
|
||||
this.scene.load.start();
|
||||
return loadPromise;
|
||||
}
|
||||
|
||||
getMenuImages(): Promise<RadialMenuItem[]> {
|
||||
const promises = [];
|
||||
for (const key in emotes) {
|
||||
const promise = this.lazyLoadEmoteTexture(key).then((textureKey) => {
|
||||
return {
|
||||
image: textureKey,
|
||||
name: textureKey,
|
||||
};
|
||||
});
|
||||
promises.push(promise);
|
||||
}
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.subscription.unsubscribe();
|
||||
|
|
|
@ -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, emoteMenuStore } from "../../Stores/EmoteStore";
|
||||
import {
|
||||
audioManagerFileStore,
|
||||
audioManagerVisibilityStore,
|
||||
|
@ -93,8 +94,10 @@ import { userIsAdminStore } from "../../Stores/GameStore";
|
|||
import { layoutManagerActionStore } from "../../Stores/LayoutManagerStore";
|
||||
import { EmbeddedWebsiteManager } from "./EmbeddedWebsiteManager";
|
||||
import { GameMapPropertiesListener } from "./GameMapPropertiesListener";
|
||||
import { analyticsClient } from "../../Administration/AnalyticsClient";
|
||||
import { get } from "svelte/store";
|
||||
import type { RadialMenuItem } from "../Components/RadialMenu";
|
||||
import {analyticsClient} from "../../Administration/AnalyticsClient";
|
||||
import { contactPageStore } from "../../Stores/MenuStore";
|
||||
|
||||
export interface GameSceneInitInterface {
|
||||
initPosition: PointInterface | null;
|
||||
|
@ -172,6 +175,8 @@ export class GameScene extends DirtyScene {
|
|||
private iframeSubscriptionList!: Array<Subscription>;
|
||||
private peerStoreUnsubscribe!: () => void;
|
||||
private chatVisibilityUnsubscribe!: () => void;
|
||||
private emoteUnsubscribe!: () => void;
|
||||
private emoteMenuUnsubscribe!: () => void;
|
||||
private biggestAvailableAreaStoreUnsubscribe!: () => void;
|
||||
MapUrlFile: string;
|
||||
roomUrl: string;
|
||||
|
@ -428,6 +433,7 @@ export class GameScene extends DirtyScene {
|
|||
gameManager.gameSceneIsCreated(this);
|
||||
urlManager.pushRoomIdToUrl(this.room);
|
||||
analyticsClient.enteredRoom(this.room.id);
|
||||
contactPageStore.set(this.room.contactPage);
|
||||
|
||||
if (touchScreenManager.supportTouchScreen) {
|
||||
this.pinchManager = new PinchManager(this);
|
||||
|
@ -616,6 +622,22 @@ export class GameScene extends DirtyScene {
|
|||
this.openChatIcon.setVisible(!v);
|
||||
});
|
||||
|
||||
this.emoteUnsubscribe = emoteStore.subscribe((emoteKey) => {
|
||||
if (emoteKey) {
|
||||
this.CurrentPlayer?.playEmote(emoteKey);
|
||||
this.connection?.emitEmoteEvent(emoteKey);
|
||||
emoteStore.set(null);
|
||||
}
|
||||
});
|
||||
|
||||
this.emoteMenuUnsubscribe = emoteMenuStore.subscribe((emoteMenu) => {
|
||||
if (emoteMenu) {
|
||||
this.userInputManager.disableControls();
|
||||
} else {
|
||||
this.userInputManager.restoreControls();
|
||||
}
|
||||
});
|
||||
|
||||
Promise.all([this.connectionAnswerPromise as Promise<unknown>, ...scriptPromises]).then(() => {
|
||||
this.scene.wake();
|
||||
});
|
||||
|
@ -1306,6 +1328,8 @@ ${escapedMessage}
|
|||
this.emoteManager.destroy();
|
||||
this.peerStoreUnsubscribe();
|
||||
this.chatVisibilityUnsubscribe();
|
||||
this.emoteUnsubscribe();
|
||||
this.emoteMenuUnsubscribe();
|
||||
this.biggestAvailableAreaStoreUnsubscribe();
|
||||
iframeListener.unregisterAnswerer("getState");
|
||||
iframeListener.unregisterAnswerer("loadTileset");
|
||||
|
@ -1436,9 +1460,13 @@ ${escapedMessage}
|
|||
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.emoteManager
|
||||
.getMenuImages()
|
||||
.then((emoteMenuElements) => this.CurrentPlayer.openOrCloseEmoteMenu(emoteMenuElements));
|
||||
|
||||
// toggle EmoteMenu
|
||||
if (get(emoteMenuStore)) {
|
||||
emoteMenuStore.closeEmoteMenu();
|
||||
} else {
|
||||
emoteMenuStore.openEmoteMenu();
|
||||
}
|
||||
});
|
||||
this.CurrentPlayer.on(requestEmoteEventName, (emoteKey: string) => {
|
||||
this.connection?.emitEmoteEvent(emoteKey);
|
||||
|
|
|
@ -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 { RadialMenu, RadialMenuClickEvent, RadialMenuItem } from "../Components/RadialMenu";
|
||||
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: RadialMenu | 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 {
|
||||
|
@ -93,40 +84,4 @@ export class Player extends Character {
|
|||
public isMoving(): boolean {
|
||||
return this.wasMoving;
|
||||
}
|
||||
|
||||
openOrCloseEmoteMenu(emotes: RadialMenuItem[]) {
|
||||
if (this.emoteMenu) {
|
||||
this.closeEmoteMenu();
|
||||
} else {
|
||||
this.openEmoteMenu(emotes);
|
||||
}
|
||||
}
|
||||
|
||||
openEmoteMenu(emotes: RadialMenuItem[]): void {
|
||||
this.cancelPreviousEmote();
|
||||
this.emoteMenu = new RadialMenu(this.scene, this.x, this.y, emotes);
|
||||
this.emoteMenu.on(RadialMenuClickEvent, (item: RadialMenuItem) => {
|
||||
this.closeEmoteMenu();
|
||||
this.emit(requestEmoteEventName, item.name);
|
||||
this.playEmote(item.name);
|
||||
});
|
||||
}
|
||||
|
||||
isSilent() {
|
||||
super.isSilent();
|
||||
}
|
||||
noSilent() {
|
||||
super.noSilent();
|
||||
}
|
||||
|
||||
closeEmoteMenu(): void {
|
||||
if (!this.emoteMenu) return;
|
||||
this.emoteMenu.destroy();
|
||||
this.emoteMenu = null;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.scene.events.removeListener("postupdate", this.updateListener);
|
||||
super.destroy();
|
||||
}
|
||||
}
|
||||
|
|
18
front/src/Stores/EmoteStore.ts
Normal file
18
front/src/Stores/EmoteStore.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { writable } from "svelte/store";
|
||||
|
||||
function createEmoteMenuStore() {
|
||||
const { subscribe, set } = writable(false);
|
||||
|
||||
return {
|
||||
subscribe,
|
||||
openEmoteMenu() {
|
||||
set(true);
|
||||
},
|
||||
closeEmoteMenu() {
|
||||
set(false);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export const emoteStore = writable<string | null>(null);
|
||||
export const emoteMenuStore = createEmoteMenuStore();
|
|
@ -289,7 +289,9 @@ export const mediaStreamConstraintsStore = derived(
|
|||
// Disable webcam for energy reasons (the user is not moving and we are talking to no one)
|
||||
if ($cameraEnergySavingStore === true) {
|
||||
currentVideoConstraint = false;
|
||||
currentAudioConstraint = false;
|
||||
//this optimization is desactivated because of sound issues on chrome
|
||||
//todo: fix this conflicts and reactivate this optimization
|
||||
//currentAudioConstraint = false;
|
||||
}
|
||||
|
||||
// Let's make the changes only if the new value is different from the old one.
|
||||
|
@ -376,7 +378,8 @@ function toggleConstraints(track: MediaStreamTrack, constraints: MediaTrackConst
|
|||
} else if (constraints === false) {
|
||||
track.stop();
|
||||
}
|
||||
if (constraints && constraints !== true) {
|
||||
// @ts-ignore
|
||||
if (typeof constraints !== "boolean" && constraints !== true) {
|
||||
track.applyConstraints(constraints);
|
||||
}
|
||||
}
|
||||
|
@ -391,12 +394,11 @@ export const localStreamStore = derived<Readable<MediaStreamConstraints>, LocalS
|
|||
|
||||
async function initStream(constraints: MediaStreamConstraints) {
|
||||
try {
|
||||
const newStream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||
if (currentStream) {
|
||||
//we need stop all tracks to make sure the old stream will be garbage collected
|
||||
currentStream.getTracks().forEach((t) => t.stop());
|
||||
//currentStream.getTracks().forEach((t) => t.stop());
|
||||
}
|
||||
currentStream = newStream;
|
||||
currentStream = await navigator.mediaDevices.getUserMedia(constraints);
|
||||
set({
|
||||
type: "success",
|
||||
stream: currentStream,
|
||||
|
@ -480,7 +482,8 @@ export const localStreamStore = derived<Readable<MediaStreamConstraints>, LocalS
|
|||
type: "success",
|
||||
stream: null,
|
||||
});
|
||||
} else if ((constraints.audio && !oldConstraints.audio) || (!oldConstraints.video && constraints.video)) {
|
||||
} //we reemit the stream if it was muted just to be sure
|
||||
else if (constraints.audio /* && !oldConstraints.audio*/ || (!oldConstraints.video && constraints.video)) {
|
||||
initStream(constraints);
|
||||
}
|
||||
oldConstraints = {
|
||||
|
|
|
@ -2,13 +2,13 @@ import { get, writable } from "svelte/store";
|
|||
import Timeout = NodeJS.Timeout;
|
||||
import { userIsAdminStore } from "./GameStore";
|
||||
import { CONTACT_URL } from "../Enum/EnvironmentVariable";
|
||||
import {analyticsClient} from "../Administration/AnalyticsClient";
|
||||
import { analyticsClient } from "../Administration/AnalyticsClient";
|
||||
|
||||
export const menuIconVisiblilityStore = writable(false);
|
||||
export const menuVisiblilityStore = writable(false);
|
||||
menuVisiblilityStore.subscribe((value) => {
|
||||
if (value) analyticsClient.openedMenu();
|
||||
})
|
||||
});
|
||||
export const menuInputFocusStore = writable(false);
|
||||
export const userIsConnected = writable(false);
|
||||
|
||||
|
@ -74,13 +74,18 @@ function createSubMenusStore() {
|
|||
|
||||
export const subMenusStore = createSubMenusStore();
|
||||
|
||||
export const contactPageStore = writable<string | undefined>(CONTACT_URL);
|
||||
|
||||
export function checkSubMenuToShow() {
|
||||
if (!get(userIsAdminStore)) {
|
||||
subMenusStore.removeMenu(SubMenusInterface.globalMessages);
|
||||
subMenusStore.removeMenu(SubMenusInterface.globalMessages);
|
||||
subMenusStore.removeMenu(SubMenusInterface.contact);
|
||||
|
||||
if (get(userIsAdminStore)) {
|
||||
subMenusStore.addMenu(SubMenusInterface.globalMessages);
|
||||
}
|
||||
|
||||
if (CONTACT_URL === undefined) {
|
||||
subMenusStore.removeMenu(SubMenusInterface.contact);
|
||||
if (get(contactPageStore) !== undefined) {
|
||||
subMenusStore.addMenu(SubMenusInterface.contact);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import { Game } from "./Phaser/Game/Game";
|
|||
import App from "./Components/App.svelte";
|
||||
import { HtmlUtils } from "./WebRtc/HtmlUtils";
|
||||
import WebGLRenderer = Phaser.Renderer.WebGL.WebGLRenderer;
|
||||
import {analyticsClient} from "./Administration/AnalyticsClient";
|
||||
import { analyticsClient } from "./Administration/AnalyticsClient";
|
||||
|
||||
const { width, height } = coWebsiteManager.getGameSize();
|
||||
const valueGameQuality = localUserStore.getGameQualityValue();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue